Showing preview only (3,802K chars total). Download the full file or copy to clipboard to get everything.
Repository: googlecloudrobotics/core
Branch: main
Commit: 086c5c4d4aeb
Files: 462
Total size: 3.5 MB
Directory structure:
gitextract_mwr70acn/
├── .bazelignore
├── .bazelrc
├── .bazelversion
├── .dockerignore
├── .editorconfig
├── .github/
│ ├── ci/
│ │ ├── .bazelrc
│ │ ├── Dockerfile.integration-test-image
│ │ ├── common.sh
│ │ ├── deploy_navtest.sh
│ │ ├── deploy_navtest_cloudbuild.yaml
│ │ ├── deployments/
│ │ │ ├── robco-integration-test/
│ │ │ │ ├── config.sh
│ │ │ │ └── kubernetes/
│ │ │ │ └── k8s-relay-rollout.yaml
│ │ │ └── robco-navtest/
│ │ │ └── config.sh
│ │ ├── integration_test.sh
│ │ ├── integration_test_cloudbuild.yaml
│ │ ├── integration_test_image_builder.sh
│ │ ├── presubmit.sh
│ │ └── release_binary.sh
│ ├── dependabot.yml
│ └── workflows/
│ ├── check-bazel.yml
│ ├── postsubmit.yml
│ ├── presubmit.yml
│ └── release.yml
├── .gitignore
├── .pep8
├── BUILD.bazel
├── CONTRIBUTING.md
├── LICENSE
├── METADATA
├── MODULE.bazel
├── README.md
├── bazel/
│ ├── BUILD.bazel
│ ├── BUILD.sysroot
│ ├── app.bzl
│ ├── app_chart.bzl
│ ├── build_rules/
│ │ ├── app_chart/
│ │ │ ├── BUILD.bazel
│ │ │ ├── Chart.yaml.template
│ │ │ ├── cache_gcr_credentials.bzl
│ │ │ ├── cache_gcr_credentials.sh.tpl
│ │ │ ├── push_all.bzl
│ │ │ ├── push_all.sh.tpl
│ │ │ ├── run_parallel.bzl
│ │ │ ├── run_parallel.sh.tpl
│ │ │ ├── values-cloud.yaml
│ │ │ └── values-robot.yaml
│ │ ├── copy.bzl
│ │ ├── helm_chart.bzl
│ │ └── helm_template.bzl
│ ├── container_push.bzl
│ └── debug_repository.bzl
├── config.sh.tmpl
├── current_versions.txt
├── deploy.sh
├── docs/
│ ├── .gitignore
│ ├── _config.yml
│ ├── concepts/
│ │ ├── app-management.md
│ │ ├── config.md
│ │ ├── device_identity.md
│ │ └── federation.md
│ ├── developers/
│ │ └── debug-auth.md
│ ├── how-to/
│ │ ├── connecting-robot.md
│ │ ├── creating-declarative-api.md
│ │ ├── deploy-from-sources.md
│ │ ├── deploying-grpc-service.md
│ │ ├── deploying-service.md
│ │ ├── examples/
│ │ │ ├── charge-service/
│ │ │ │ ├── Dockerfile
│ │ │ │ ├── charge-action.yaml
│ │ │ │ ├── charge-controller.yaml
│ │ │ │ ├── charge-crd.yaml
│ │ │ │ └── server.py
│ │ │ ├── greeter-service/
│ │ │ │ ├── Makefile
│ │ │ │ ├── client/
│ │ │ │ │ ├── Dockerfile
│ │ │ │ │ └── client.cc
│ │ │ │ ├── deploy.sh
│ │ │ │ ├── greeter-server.yaml.tmpl
│ │ │ │ ├── proto/
│ │ │ │ │ └── helloworld.proto
│ │ │ │ └── server/
│ │ │ │ ├── Dockerfile
│ │ │ │ └── server.cc
│ │ │ └── hello-service/
│ │ │ ├── client/
│ │ │ │ ├── Dockerfile
│ │ │ │ └── client.py
│ │ │ └── server/
│ │ │ ├── Dockerfile
│ │ │ ├── hello-server.yaml
│ │ │ └── server.py
│ │ ├── running-ros-node.md
│ │ ├── setting-up-oauth.md
│ │ └── using-cloud-storage.md
│ ├── index.md
│ ├── overview.md
│ └── quickstart.md
├── new_versions.txt
├── non_module_deps.bzl
├── nvchecker.toml
├── scripts/
│ ├── BUILD.bazel
│ ├── backup_robots.sh
│ ├── check-images.sh
│ ├── common.sh
│ ├── config.sh
│ ├── include-config.sh
│ ├── migrate.sh
│ ├── pre-commit
│ ├── robot-sim.sh
│ └── set-config.sh
├── src/
│ ├── .gitignore
│ ├── BUILD.bazel
│ ├── README.md
│ ├── app_charts/
│ │ ├── BUILD.bazel
│ │ ├── README.md
│ │ ├── akri/
│ │ │ ├── BUILD.bazel
│ │ │ ├── akri-robot.values.yaml
│ │ │ ├── robot/
│ │ │ │ └── akri.yaml
│ │ │ └── values-robot.yaml
│ │ ├── base/
│ │ │ ├── BUILD.bazel
│ │ │ ├── README.md
│ │ │ ├── app_management_test.sh
│ │ │ ├── cert-manager-cloud.values.yaml
│ │ │ ├── cert-manager-google-cas-issuer-cloud.values.yaml
│ │ │ ├── cert-manager-robot.values.yaml
│ │ │ ├── cloud/
│ │ │ │ ├── app-management-policy.yaml
│ │ │ │ ├── app-management.yaml
│ │ │ │ ├── apps-crd.yaml
│ │ │ │ ├── cert-ingress.yaml
│ │ │ │ ├── cert-manager-certificates.yaml
│ │ │ │ ├── cert-manager-google-cas-issuer.yaml
│ │ │ │ ├── cert-manager-issuers.yaml
│ │ │ │ ├── cert-manager.yaml
│ │ │ │ ├── cr-syncer-auth-webhook.yaml
│ │ │ │ ├── cr-syncer-policy.yaml
│ │ │ │ ├── domain-redirect.yaml
│ │ │ │ ├── fluentd-metrics.yaml
│ │ │ │ ├── kubernetes-api.yaml
│ │ │ │ ├── namespace.yaml
│ │ │ │ ├── nginx-ingress-controller-policy.yaml
│ │ │ │ ├── nginx-ingress-controller.yaml
│ │ │ │ ├── oauth2-proxy.yaml
│ │ │ │ ├── registry-crd.yaml
│ │ │ │ ├── registry-policy.yaml
│ │ │ │ ├── relay-dashboards.yaml
│ │ │ │ ├── token-vendor-app-fwd.yaml
│ │ │ │ └── token-vendor-rollout.yaml
│ │ │ ├── fluent-bit-helm.sh
│ │ │ ├── fluent-bit-values.yaml
│ │ │ ├── relay-dashboard.json
│ │ │ ├── robot/
│ │ │ │ ├── app-management.yaml
│ │ │ │ ├── cert-manager-certificates.yaml
│ │ │ │ ├── cert-manager-issuers.yaml
│ │ │ │ ├── cert-manager.yaml
│ │ │ │ ├── cr-syncer.yaml
│ │ │ │ ├── fluent-bit.yaml
│ │ │ │ ├── fluentd-gcp-addon.yaml
│ │ │ │ ├── fluentd-metrics.yaml
│ │ │ │ ├── gcr-credential-refresher.yaml
│ │ │ │ └── metadata-server.yaml
│ │ │ ├── values-cloud.yaml
│ │ │ └── values-robot.yaml
│ │ ├── k8s-relay/
│ │ │ ├── BUILD.bazel
│ │ │ ├── cloud/
│ │ │ │ ├── ingress.yaml
│ │ │ │ ├── kubernetes-relay-server.yaml
│ │ │ │ ├── service-monitor.yaml
│ │ │ │ └── service.yaml
│ │ │ ├── robot/
│ │ │ │ └── kubernetes-relay-client.yaml
│ │ │ ├── values-cloud.yaml
│ │ │ └── values-robot.yaml
│ │ ├── mission-crd/
│ │ │ ├── BUILD.bazel
│ │ │ ├── mission_crd.yaml
│ │ │ └── values.yaml
│ │ ├── platform-apps/
│ │ │ ├── BUILD.bazel
│ │ │ └── values.yaml
│ │ ├── prometheus/
│ │ │ ├── BUILD.bazel
│ │ │ ├── README.md
│ │ │ ├── cloud/
│ │ │ │ ├── app.yaml
│ │ │ │ ├── base-alerts.yaml
│ │ │ │ ├── federation-service-monitor.yaml
│ │ │ │ ├── grafana-ingress.yaml
│ │ │ │ ├── prometheus-ingress.yaml
│ │ │ │ ├── prometheus-operator.yaml
│ │ │ │ ├── prometheus-relay.yaml
│ │ │ │ └── storage-class.yaml
│ │ │ ├── prometheus-cloud.values.yaml
│ │ │ ├── prometheus-robot.values.yaml
│ │ │ ├── robot/
│ │ │ │ ├── hw-exporter.yaml
│ │ │ │ ├── prometheus-adapter.yaml
│ │ │ │ ├── prometheus-operator.yaml
│ │ │ │ ├── prometheus-relay-client.yaml
│ │ │ │ └── smartctl-exporter.yaml
│ │ │ ├── update_prometheus_adapter.sh
│ │ │ └── values-cloud.yaml
│ │ └── token-vendor/
│ │ ├── BUILD.bazel
│ │ ├── cloud/
│ │ │ ├── dashboard.yaml
│ │ │ ├── ingress.yaml
│ │ │ ├── service-monitor.yaml
│ │ │ ├── service.yaml
│ │ │ ├── token-vendor-policy.yaml
│ │ │ └── token-vendor.yaml
│ │ └── dashboard.json
│ ├── bootstrap/
│ │ ├── cloud/
│ │ │ ├── BUILD.bazel
│ │ │ ├── INSTALL_FROM_BINARY
│ │ │ ├── run-install.sh
│ │ │ └── terraform/
│ │ │ ├── .gitignore
│ │ │ ├── BUILD.bazel
│ │ │ ├── README.md
│ │ │ ├── address.tf
│ │ │ ├── certificate-authority.tf
│ │ │ ├── cluster.tf
│ │ │ ├── dns.tf
│ │ │ ├── endpoints.tf
│ │ │ ├── gcs.tf
│ │ │ ├── input.tf
│ │ │ ├── logging.tf
│ │ │ ├── multi-cluster-ingress.tf
│ │ │ ├── output.tf
│ │ │ ├── project.tf
│ │ │ ├── provider.tf
│ │ │ ├── registry.tf
│ │ │ ├── service-account.tf
│ │ │ ├── versions.tf
│ │ │ ├── workload-identity.tf
│ │ │ └── www.yaml
│ │ └── robot/
│ │ ├── BUILD.bazel
│ │ └── setup_robot.sh
│ ├── go/
│ │ ├── cmd/
│ │ │ ├── app-rollout-controller/
│ │ │ │ ├── BUILD.bazel
│ │ │ │ └── main.go
│ │ │ ├── chart-assignment-controller/
│ │ │ │ ├── BUILD.bazel
│ │ │ │ └── main.go
│ │ │ ├── cr-syncer/
│ │ │ │ ├── BUILD.bazel
│ │ │ │ ├── health.go
│ │ │ │ ├── health_test.go
│ │ │ │ ├── main.go
│ │ │ │ ├── main_test.go
│ │ │ │ ├── syncer.go
│ │ │ │ └── syncer_test.go
│ │ │ ├── cr-syncer-auth-webhook/
│ │ │ │ ├── BUILD.bazel
│ │ │ │ ├── main.go
│ │ │ │ ├── request.go
│ │ │ │ └── request_test.go
│ │ │ ├── gcr-credential-refresher/
│ │ │ │ ├── BUILD.bazel
│ │ │ │ └── main.go
│ │ │ ├── http-relay-client/
│ │ │ │ ├── BUILD.bazel
│ │ │ │ ├── client/
│ │ │ │ │ ├── BUILD.bazel
│ │ │ │ │ ├── client.go
│ │ │ │ │ └── client_test.go
│ │ │ │ └── main.go
│ │ │ ├── http-relay-server/
│ │ │ │ ├── BUILD.bazel
│ │ │ │ ├── README.md
│ │ │ │ ├── main.go
│ │ │ │ └── server/
│ │ │ │ ├── BUILD.bazel
│ │ │ │ ├── broker.go
│ │ │ │ ├── broker_test.go
│ │ │ │ ├── server.go
│ │ │ │ └── server_test.go
│ │ │ ├── hw-exporter/
│ │ │ │ ├── BUILD.bazel
│ │ │ │ ├── main.go
│ │ │ │ └── main_test.go
│ │ │ ├── metadata-server/
│ │ │ │ ├── BUILD.bazel
│ │ │ │ ├── coredns.go
│ │ │ │ ├── coredns_test.go
│ │ │ │ ├── main.go
│ │ │ │ ├── main_test.go
│ │ │ │ ├── metadata.go
│ │ │ │ ├── metadata_test.go
│ │ │ │ └── nftables.go
│ │ │ ├── setup-dev/
│ │ │ │ ├── BUILD.bazel
│ │ │ │ ├── main.go
│ │ │ │ └── setup-dev.md
│ │ │ ├── setup-robot/
│ │ │ │ ├── BUILD.bazel
│ │ │ │ ├── main.go
│ │ │ │ └── main_test.go
│ │ │ ├── synk/
│ │ │ │ ├── BUILD.bazel
│ │ │ │ ├── README.md
│ │ │ │ └── synk.go
│ │ │ └── token-vendor/
│ │ │ ├── BUILD.bazel
│ │ │ ├── README.md
│ │ │ ├── api/
│ │ │ │ ├── BUILD.bazel
│ │ │ │ ├── api.go
│ │ │ │ └── v1/
│ │ │ │ ├── BUILD.bazel
│ │ │ │ ├── testdata/
│ │ │ │ │ ├── README.md
│ │ │ │ │ ├── cloudiot/
│ │ │ │ │ │ ├── describe_device.json
│ │ │ │ │ │ └── describe_device_expired_key.json
│ │ │ │ │ ├── rsa_cert.pem
│ │ │ │ │ └── rsa_private.pem
│ │ │ │ ├── v1.go
│ │ │ │ └── v1_test.go
│ │ │ ├── app/
│ │ │ │ ├── BUILD.bazel
│ │ │ │ ├── tokenvendor.go
│ │ │ │ └── tokenvendor_test.go
│ │ │ ├── main.go
│ │ │ ├── oauth/
│ │ │ │ ├── BUILD.bazel
│ │ │ │ ├── cache.go
│ │ │ │ ├── cache_test.go
│ │ │ │ ├── jwt/
│ │ │ │ │ ├── BUILD.bazel
│ │ │ │ │ ├── jwt.go
│ │ │ │ │ └── jwt_test.go
│ │ │ │ └── verifier.go
│ │ │ ├── repository/
│ │ │ │ ├── BUILD.bazel
│ │ │ │ ├── k8s/
│ │ │ │ │ ├── BUILD.bazel
│ │ │ │ │ ├── k8s.go
│ │ │ │ │ └── k8s_test.go
│ │ │ │ ├── memory/
│ │ │ │ │ ├── BUILD.bazel
│ │ │ │ │ ├── memory.go
│ │ │ │ │ └── memory_test.go
│ │ │ │ └── repository.go
│ │ │ ├── testdata/
│ │ │ │ ├── describe_device_a.json
│ │ │ │ ├── describe_device_b.json
│ │ │ │ ├── describe_device_b_blocked.json
│ │ │ │ └── list_devices.json
│ │ │ └── tokensource/
│ │ │ ├── BUILD.bazel
│ │ │ ├── gcp.go
│ │ │ └── gcp_test.go
│ │ ├── generate.sh
│ │ ├── pkg/
│ │ │ ├── apis/
│ │ │ │ ├── apps/
│ │ │ │ │ └── v1alpha1/
│ │ │ │ │ ├── BUILD.bazel
│ │ │ │ │ ├── doc.go
│ │ │ │ │ ├── register.go
│ │ │ │ │ ├── types.go
│ │ │ │ │ └── zz_generated.deepcopy.go
│ │ │ │ └── registry/
│ │ │ │ └── v1alpha1/
│ │ │ │ ├── BUILD.bazel
│ │ │ │ ├── doc.go
│ │ │ │ ├── register.go
│ │ │ │ ├── types.go
│ │ │ │ └── zz_generated.deepcopy.go
│ │ │ ├── client/
│ │ │ │ ├── informers/
│ │ │ │ │ ├── BUILD.bazel
│ │ │ │ │ ├── apps/
│ │ │ │ │ │ ├── BUILD.bazel
│ │ │ │ │ │ ├── interface.go
│ │ │ │ │ │ └── v1alpha1/
│ │ │ │ │ │ ├── BUILD.bazel
│ │ │ │ │ │ ├── app.go
│ │ │ │ │ │ ├── approllout.go
│ │ │ │ │ │ ├── chartassignment.go
│ │ │ │ │ │ ├── interface.go
│ │ │ │ │ │ └── resourceset.go
│ │ │ │ │ ├── factory.go
│ │ │ │ │ ├── generic.go
│ │ │ │ │ ├── internalinterfaces/
│ │ │ │ │ │ ├── BUILD.bazel
│ │ │ │ │ │ └── factory_interfaces.go
│ │ │ │ │ └── registry/
│ │ │ │ │ ├── BUILD.bazel
│ │ │ │ │ ├── interface.go
│ │ │ │ │ └── v1alpha1/
│ │ │ │ │ ├── BUILD.bazel
│ │ │ │ │ ├── interface.go
│ │ │ │ │ └── robot.go
│ │ │ │ ├── listers/
│ │ │ │ │ ├── apps/
│ │ │ │ │ │ └── v1alpha1/
│ │ │ │ │ │ ├── BUILD.bazel
│ │ │ │ │ │ ├── app.go
│ │ │ │ │ │ ├── approllout.go
│ │ │ │ │ │ ├── chartassignment.go
│ │ │ │ │ │ ├── expansion_generated.go
│ │ │ │ │ │ └── resourceset.go
│ │ │ │ │ └── registry/
│ │ │ │ │ └── v1alpha1/
│ │ │ │ │ ├── BUILD.bazel
│ │ │ │ │ ├── expansion_generated.go
│ │ │ │ │ └── robot.go
│ │ │ │ └── versioned/
│ │ │ │ ├── BUILD.bazel
│ │ │ │ ├── clientset.go
│ │ │ │ ├── doc.go
│ │ │ │ ├── fake/
│ │ │ │ │ ├── BUILD.bazel
│ │ │ │ │ ├── clientset_generated.go
│ │ │ │ │ ├── doc.go
│ │ │ │ │ └── register.go
│ │ │ │ ├── scheme/
│ │ │ │ │ ├── BUILD.bazel
│ │ │ │ │ ├── doc.go
│ │ │ │ │ └── register.go
│ │ │ │ └── typed/
│ │ │ │ ├── apps/
│ │ │ │ │ └── v1alpha1/
│ │ │ │ │ ├── BUILD.bazel
│ │ │ │ │ ├── app.go
│ │ │ │ │ ├── approllout.go
│ │ │ │ │ ├── apps_client.go
│ │ │ │ │ ├── chartassignment.go
│ │ │ │ │ ├── doc.go
│ │ │ │ │ ├── fake/
│ │ │ │ │ │ ├── BUILD.bazel
│ │ │ │ │ │ ├── doc.go
│ │ │ │ │ │ ├── fake_app.go
│ │ │ │ │ │ ├── fake_approllout.go
│ │ │ │ │ │ ├── fake_apps_client.go
│ │ │ │ │ │ ├── fake_chartassignment.go
│ │ │ │ │ │ └── fake_resourceset.go
│ │ │ │ │ ├── generated_expansion.go
│ │ │ │ │ └── resourceset.go
│ │ │ │ └── registry/
│ │ │ │ └── v1alpha1/
│ │ │ │ ├── BUILD.bazel
│ │ │ │ ├── doc.go
│ │ │ │ ├── fake/
│ │ │ │ │ ├── BUILD.bazel
│ │ │ │ │ ├── doc.go
│ │ │ │ │ ├── fake_registry_client.go
│ │ │ │ │ └── fake_robot.go
│ │ │ │ ├── generated_expansion.go
│ │ │ │ ├── registry_client.go
│ │ │ │ └── robot.go
│ │ │ ├── configutil/
│ │ │ │ ├── BUILD.bazel
│ │ │ │ ├── config_reader.go
│ │ │ │ └── config_reader_test.go
│ │ │ ├── controller/
│ │ │ │ ├── approllout/
│ │ │ │ │ ├── BUILD.bazel
│ │ │ │ │ ├── controller.go
│ │ │ │ │ └── controller_test.go
│ │ │ │ └── chartassignment/
│ │ │ │ ├── BUILD.bazel
│ │ │ │ ├── controller.go
│ │ │ │ ├── release.go
│ │ │ │ ├── release_test.go
│ │ │ │ ├── validator.go
│ │ │ │ └── validator_test.go
│ │ │ ├── gcr/
│ │ │ │ ├── BUILD.bazel
│ │ │ │ ├── update_gcr_credential_test.go
│ │ │ │ └── update_gcr_credentials.go
│ │ │ ├── kubetest/
│ │ │ │ ├── BUILD.bazel
│ │ │ │ └── kubetest.go
│ │ │ ├── kubeutils/
│ │ │ │ ├── BUILD.bazel
│ │ │ │ └── kubeutils.go
│ │ │ ├── robotauth/
│ │ │ │ ├── BUILD.bazel
│ │ │ │ ├── robotauth.go
│ │ │ │ └── robotauth_test.go
│ │ │ ├── setup/
│ │ │ │ ├── BUILD.bazel
│ │ │ │ ├── setupcommon.go
│ │ │ │ ├── setupcommon_test.go
│ │ │ │ └── util/
│ │ │ │ ├── BUILD.bazel
│ │ │ │ ├── factory.go
│ │ │ │ └── fake.go
│ │ │ └── synk/
│ │ │ ├── BUILD.bazel
│ │ │ ├── interface.go
│ │ │ ├── sort.go
│ │ │ ├── sort_test.go
│ │ │ ├── synk.go
│ │ │ └── synk_test.go
│ │ └── tests/
│ │ ├── BUILD.bazel
│ │ ├── apps/
│ │ │ ├── BUILD.bazel
│ │ │ ├── apps_test.go
│ │ │ └── run.sh
│ │ ├── k8s_integration_test.go
│ │ ├── k8s_integration_test_auth_helper.go
│ │ ├── relay/
│ │ │ ├── BUILD.bazel
│ │ │ ├── in_process_relay_test.go
│ │ │ └── nok8s_relay_test.go
│ │ ├── relay-bench.sh
│ │ └── relay_test.sh
│ ├── go.mod
│ ├── go.sum
│ ├── gomod.sh
│ └── proto/
│ └── http-relay/
│ ├── BUILD.bazel
│ ├── http_over_rpc.proto
│ └── unused.go
└── third_party/
├── BUILD
├── BUILD.bazel
├── README.md
├── akri/
│ ├── BUILD.bazel
│ ├── akri-0.12.9.tgz
│ ├── akri-configuration-crd.yaml
│ ├── akri-instance-crd.yaml
│ └── update-akri.sh
├── app_crd.BUILD
├── cert-manager/
│ ├── BUILD.bazel
│ └── cert-manager-v1.16.3.tgz
├── cert-manager-google-cas-issuer/
│ ├── BUILD.bazel
│ └── cert-manager-google-cas-issuer-v0.6.2.tgz
├── fluentd_gcp_addon/
│ ├── BUILD.bazel
│ ├── fluentd-gcp-configmap.yaml
│ └── fluentd-gcp-ds.yaml
├── helm2/
│ └── BUILD.bazel
├── helm3/
│ └── BUILD.bazel
├── ingress-nginx.BUILD
├── kube-prometheus-stack/
│ ├── 00-crds.yaml
│ ├── 01-crds.yaml
│ ├── BUILD.bazel
│ ├── kube-prometheus-stack-72.9.1.tgz
│ └── update_crd.sh
├── kubernetes_proto/
│ ├── meta/
│ │ ├── BUILD.bazel
│ │ ├── README.md
│ │ └── generated.proto
│ ├── runtime/
│ │ ├── BUILD.bazel
│ │ └── generated.proto
│ └── schema/
│ ├── BUILD.bazel
│ └── generated.proto
└── terraform.BUILD
================================================
FILE CONTENTS
================================================
================================================
FILE: .bazelignore
================================================
src/.gopath
================================================
FILE: .bazelrc
================================================
# Enable Bzlmod for every Bazel command
common --enable_bzlmod
# Work around go issue with LLVM 15+: https://github.com/bazelbuild/rules_go/issues/3691#issuecomment-2263999685
build --@io_bazel_rules_go//go/config:linkmode=pie
# Enforce stricter environment rules, which eliminates some non-hermetic
# behavior and therefore improves both the remote_cache cache hit rate and the
# correctness and repeatability of the build.
build --incompatible_strict_action_env=true
# Make sure that no regressions are introduced until the flag is flipped
# See: https://github.com/bazelbuild/bazel/issues/8195
build --incompatible_disallow_empty_glob
# Use the new paths.
# https://github.com/bazelbuild/bazel/issues/23127
common --incompatible_use_plus_in_repo_names
# Always use the pre-configured toolchain.
build --repo_env=BAZEL_DO_NOT_DETECT_CPP_TOOLCHAIN=1
build --action_env=BAZEL_DO_NOT_DETECT_CPP_TOOLCHAIN=1
# Set a higher timeout value, just in case.
build --remote_timeout=3600
# Platform flags
# The toolchain container used for execution is defined in the target indicated
# by "extra_execution_platforms", "host_platform" and "platforms".
# More about platforms: https://docs.bazel.build/versions/master/platforms.html
build:linux_x86_64 --extra_execution_platforms=//bazel:linux_x86_64
build:linux_x86_64 --host_platform=//bazel:linux_x86_64
build:linux_x86_64 --platforms=//bazel:linux_x86_64
build:remote --remote_executor=grpcs://remotebuildexecution.googleapis.com
build:remote_cache --remote_cache=grpcs://remotebuildexecution.googleapis.com
# Enable authentication. This will pick up application default credentials by
# default. You can use --google_credentials=some_file.json to use a service
# account credential instead.
build:remote --google_default_credentials=true
build:remote_cache --google_default_credentials=true
# RBE builds only support linux_x86_64.
build:remote --config=linux_x86_64
build:remote_cache --config=linux_x86_64
# Don't run integration tests and tests that need docker by default
test --test_tag_filters="-external,-requires-docker"
================================================
FILE: .bazelversion
================================================
8.4.2
================================================
FILE: .dockerignore
================================================
*
================================================
FILE: .editorconfig
================================================
# Editor configuration, see http://editorconfig.org
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true
[*.go]
indent_style = tab
indent_size = 8
[*.md]
max_line_length = off
trim_trailing_whitespace = false
================================================
FILE: .github/ci/.bazelrc
================================================
# Bazel config for CI/CD builds.
# Default to keep going
build --keep_going
# Use rbe remote execution and caching on robco-integration-test.
build --config=remote
build --remote_instance_name=projects/robco-integration-test/instances/default_instance
build --google_default_credentials=true
# Slightly higher than the numer of available remote workers (10 in default_instance).
# This has not been tuned a lot.
build --jobs=12
# No neeed to download every intermediate output to the local runner.
build --remote_download_toplevel
# Use Result Store to store Build and Test logs .
build --bes_backend=buildeventservice.googleapis.com
build --bes_results_url=https://source.cloud.google.com/results/invocations
build --bes_timeout=600s
build --bes_instance_name=robco-integration-test
# Try to mitigate DEADLINE_EXCEEDED errors (b/346715839).
# Remove experimental_ prefix when updating Bazel.
build --experimental_build_event_upload_max_retries=8
================================================
FILE: .github/ci/Dockerfile.integration-test-image
================================================
# Image used for integration_test.sh on Cloud Build.
# Allows access to GKE and to run Bazel commands.
FROM gcr.io/cloud-builders/kubectl
# Install Bazelisk
RUN \
VERSION="v1.21.0" && \
curl -L https://github.com/bazelbuild/bazelisk/releases/download/${VERSION}/bazelisk-linux-amd64 --output /usr/bin/bazelisk && \
chmod +x /usr/bin/bazelisk && \
ln -s /usr/bin/bazelisk /usr/bin/bazel
RUN mkdir -p /builder /output /workspace && chmod -R 777 /output
# rules_python is not happy if bazel runs as root so create a new user
# https://github.com/bazelbuild/rules_python/pull/713
# https://github.com/GoogleCloudPlatform/cloud-builders/issues/641
RUN adduser builder --disabled-password
# Allow running sudo without password
# Add libtinfo5, which is required locally until we can upgrade to LLVM 19
RUN apt-get update && apt-get install -y sudo libtinfo5 && apt-get clean && rm -rf /var/lib/apt/lists/* && \
usermod -aG sudo builder && \
echo "builder ALL=(ALL) NOPASSWD:ALL" > "/etc/sudoers.d/builder" && chmod 440 "/etc/sudoers.d/builder"
# For some reason //src/go/tests:go_default_test is expecting
# the kubeconfig in /home/builder/.kube/config, i.e. it does not use $HOME
# (which is /builder/home). alexanderfaxa@ could not figure out why so just
# copy the config there.
RUN mkdir -p /home/builder/.kube && \
ln -s /builder/home/.kube/config /home/builder/.kube/config
USER builder
================================================
FILE: .github/ci/common.sh
================================================
#!/bin/bash
# Format for the xtrace lines
export 'PS4=+$(date --rfc-3339=seconds):${BASH_SOURCE}:${LINENO}: '
set -o errexit # exit immediately, if a pipeline command fails
set -o pipefail # returns the last command to exit with a non-zero status
set -o xtrace # print command traces before executing command
RUNFILES_ROOT="_main"
# Wraps the common Bazel flags for CI for brevity.
function bazel_ci {
bazelisk --bazelrc="${DIR}/.bazelrc" "$@"
}
function generate_build_id() {
# Considerations for a build identifier: It must be unique, it shouldn't break
# if we try multiple dailies in a day, and it would be nice if a textual sort
# would put newest releases last.
git_hash=$(echo "$GITHUB_SHA" | cut -c1-6)
date "+daily-%Y-%m-%d-${git_hash}"
}
# Pushes images and releases a binary to a specified bucket.
# bucket: target GCS bucket to release to
# name: name of the release tar ball
# labels: optional list of filename aliases for the release, these are one-line
# text files with the release name as a bucket local path
function release_binary {
local bucket="$1"
local name="$2"
# This function is called from test and release pipelines. We (re)build the binary and push the
# app images here to ensure the app images which are referenced in the binary exist in the
# registry.
bazel_ci build \
//src/bootstrap/cloud:crc-binary \
//src/app_charts:push \
//src/go/cmd/setup-robot:setup-robot.push
# The push scripts depends on binaries in the runfiles.
local oldPwd
oldPwd=$(pwd)
# The tag variable must be called 'TAG', see cloud-robotics/bazel/container_push.bzl
for t in latest ${DOCKER_TAG}; do
cd ${oldPwd}/bazel-bin/src/go/cmd/setup-robot/push_setup-robot.push.sh.runfiles/${RUNFILES_ROOT}
${oldPwd}/bazel-bin/src/go/cmd/setup-robot/push_setup-robot.push.sh \
--repository="${CLOUD_ROBOTICS_CONTAINER_REGISTRY}/setup-robot" \
--tag="${t}"
cd ${oldPwd}/bazel-bin/src/app_charts/push.runfiles/${RUNFILES_ROOT}
TAG="$t" ${oldPwd}/bazel-bin/src/app_charts/push "${CLOUD_ROBOTICS_CONTAINER_REGISTRY}"
done
cd ${oldPwd}
gcloud storage cp \
--predefined-acl=publicRead \
bazel-bin/src/bootstrap/cloud/crc-binary.tar.gz \
"gs://${bucket}/${name}.tar.gz"
# Overwrite cache control as we want changes to run-install.sh and version files to be visible
# right away.
gcloud storage cp \
--predefined-acl=publicRead \
--cache-control="private, max-age=0, no-transform" \
src/bootstrap/cloud/run-install.sh \
"gs://${bucket}/"
# The remaining arguments are version labels. GCS does not support symlinks, so we use version
# files instead.
local vfile
vfile=$(mktemp)
echo "${name}.tar.gz" >${vfile}
shift 2
# Loop over remianing args in $* and creat alias files.
for label; do
gcloud storage cp \
--predefined-acl=publicRead \
--cache-control="private, max-age=0, no-transform" \
${vfile} "gs://${bucket}/${label}"
done
}
================================================
FILE: .github/ci/deploy_navtest.sh
================================================
#!/bin/bash
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
source "${DIR}/common.sh"
PROJECT_DIR="${DIR}/deployments/robco-navtest"
source "${PROJECT_DIR}/config.sh"
# TODO(skopecki) These variables should be declared in the run-install.sh and removed from this script.
export BUCKET_URI="https://storage.googleapis.com/robco-ci-binary-builds"
export SOURCE_CONTAINER_REGISTRY="gcr.io/robco-team"
# Deploy the binary release that was pushed by the last successful integration test.
curl --silent --show-error --fail "${BUCKET_URI}/run-install.sh" \
| bash -x -s -- ${GCP_PROJECT_ID}
================================================
FILE: .github/ci/deploy_navtest_cloudbuild.yaml
================================================
# Call deploy_navtest.sh on Cloud Build.
# TODO(b/323509860): Run directly on the Action runner when it supports WIF.
steps:
- name: "gcr.io/cloud-builders/gcloud"
entrypoint: "bash"
args: ["./.github/ci/deploy_navtest.sh"]
timeout: 1200s
================================================
FILE: .github/ci/deployments/robco-integration-test/config.sh
================================================
#!/usr/bin/env bash
# Enable cloud robotics layer 2
APP_MANAGEMENT=true
GCP_PROJECT_ID=robco-integration-test
GCP_REGION=europe-west1
GCP_ZONE=europe-west1-c
CLOUD_ROBOTICS_SHARED_OWNER_GROUP=cloud-robotics-cloud-owner-acl@twosync.google.com
CLOUD_ROBOTICS_DEPLOY_ENVIRONMENT=GCP-testing
TERRAFORM_GCS_BUCKET="robco-team-terraform-state"
TERRAFORM_GCS_PREFIX="state/${GCP_PROJECT_ID}"
CLOUD_ROBOTICS_CONTAINER_REGISTRY=gcr.io/robco-team
PRIVATE_DOCKER_PROJECTS=robco-team
CLOUD_ROBOTICS_CTX=gke_robco-integration-test_europe-west1-c_cloud-robotics
================================================
FILE: .github/ci/deployments/robco-integration-test/kubernetes/k8s-relay-rollout.yaml
================================================
apiVersion: apps.cloudrobotics.com/v1alpha1
kind: AppRollout
metadata:
name: k8s-relay
labels:
app: k8s-relay
spec:
appName: k8s-relay-dev
cloud: {}
robots:
- selector:
any: true
================================================
FILE: .github/ci/deployments/robco-navtest/config.sh
================================================
#!/usr/bin/env bash
# Enable google cloud robotics layer 2
APP_MANAGEMENT=true
GCP_PROJECT_ID=robco-navtest
GCP_REGION=europe-west1
GCP_ZONE=europe-west1-c
CLOUD_ROBOTICS_SHARED_OWNER_GROUP=cloud-robotics-cloud-owner-acl@twosync.google.com
TERRAFORM_GCS_BUCKET="robco-team-terraform-state"
TERRAFORM_GCS_PREFIX="state/${GCP_PROJECT_ID}"
CLOUD_ROBOTICS_CONTAINER_REGISTRY=gcr.io/robco-team
PRIVATE_DOCKER_PROJECTS=robco-team
CLOUD_ROBOTICS_CTX=gke_robco-navtest_europe-west1-c_cloud-robotics
================================================
FILE: .github/ci/integration_test.sh
================================================
#!/bin/bash
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
source "${DIR}/common.sh"
source "./scripts/common.sh"
# Because the format from common.sh is not recognized by Cloud Build.
export 'PS4='
# Need to source the project config from here
PROJECT_DIR="${DIR}/deployments/robco-integration-test"
source "${PROJECT_DIR}/config.sh"
gcloud config set project ${GCP_PROJECT_ID}
gke_get_credentials "${GCP_PROJECT_ID}" "cloud-robotics" "${GCP_REGION}" "${GCP_ZONE}"
BUILD_IDENTIFIER=$(generate_build_id)
echo "INFO: Build identifier is $BUILD_IDENTIFIER"
export BAZEL_FLAGS="--bazelrc=${DIR}/.bazelrc"
bash -x ./deploy.sh update "${GCP_PROJECT_ID}"
# Create a GKE cluster with a single robot for the relay test.
ROBOT_RELAY_CLUSTER="relay-test"
export SKIP_LOCAL_PULL=true
bash -x ./scripts/robot-sim.sh create "${GCP_PROJECT_ID}" "${ROBOT_RELAY_CLUSTER}"
bazel_ci run //src/go/cmd/setup-dev -- --project="${GCP_PROJECT_ID}" --robot-name="${ROBOT_RELAY_CLUSTER}"
DOMAIN=${CLOUD_ROBOTICS_DOMAIN:-"www.endpoints.${GCP_PROJECT_ID}.cloud.goog"}
ROBOT_CONTEXT="gke_${GCP_PROJECT_ID}_${GCP_ZONE}_${ROBOT_RELAY_CLUSTER}"
# Output state of cloud and robot k8s context to inspect the health of pods.
kubectl config get-contexts || true
kubectl --context ${CLOUD_ROBOTICS_CTX} get pods || true
kubectl --context ${GCP_PROJECT_ID}-robot get pods || true
kubectl --context ${ROBOT_CONTEXT} get pods || true
bazel_ci test \
--test_env GCP_PROJECT_ID=${GCP_PROJECT_ID} \
--test_env GCP_REGION=${GCP_REGION} \
--test_env GCP_ZONE=${GCP_ZONE} \
--test_env CLUSTER=${ROBOT_RELAY_CLUSTER} \
--test_env PATH=$PATH \
--jvmopt="-DCLOUD_ROBOTICS_DOMAIN=${DOMAIN}" \
--test_output=streamed \
--test_tag_filters="external" \
--strategy=TestRunner=standalone \
//...
# If this is running on main (ie, not a manual run) then update the `latest`
# binary.
if [[ "$MANUAL_RUN" == "false" ]] ; then
release_binary "robco-ci-binary-builds" "crc-${BUILD_IDENTIFIER}" "latest"
fi
================================================
FILE: .github/ci/integration_test_cloudbuild.yaml
================================================
# A Cloud Build job for running integration_test.sh.
# TODO(b/323509860): Run directly on the Action runner when it supports WIF.
steps:
# Needed for cloud build to allow running Bazel as non-root, see
# https://github.com/GoogleCloudPlatform/cloud-builders/issues/641#issuecomment-604599102
# Not part of the Dockerfile since the chmod layer adds significant image size.
- name: ubuntu
entrypoint: "bash"
args: ["-c", "chmod -R 777 /builder && chmod -R 777 /workspace"]
# This runs on a custom image that has kubectl, gcloud and bazel installed.
# See Dockerfile.integration-test-image.
- name: "gcr.io/robco-integration-test/integration-test-image@sha256:87e7cde1d2923eed014ee8ee0c365d683fa7a8a99985bbc77acb951c2e6faefc"
entrypoint: "bash"
args: ["./.github/ci/integration_test.sh"]
env:
- "GITHUB_SHA=${_GITHUB_SHA}"
- "MANUAL_RUN=${_MANUAL_RUN}"
substitutions:
_GITHUB_SHA: ""
_MANUAL_RUN: ""
options:
dynamicSubstitutions: true
substitutionOption: "MUST_MATCH"
timeout: 1800s
================================================
FILE: .github/ci/integration_test_image_builder.sh
================================================
#!/bin/bash
# Builds and pushes a docker image that can be used in Cloud Build to run
# the integration test (see integration_test_cloudbuild.yaml).
#
# To be manually invoked and the resulting sha256 copied to
# integration_test_cloudbuild.yaml after changing the Dockerfile.
set -euo pipefail
NAME="gcr.io/robco-integration-test/integration-test-image"
docker build --network=host -t "${NAME}" - \
< .github/ci/Dockerfile.integration-test-image
docker push "${NAME}"
================================================
FILE: .github/ci/presubmit.sh
================================================
#!/bin/bash
#
# Presubmit script for testing cloud robotics.
# Expected to run remotely on a GitHub Actions runner, not locally.
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
# shellcheck source=ci/common.sh
source "${DIR}/common.sh"
echo "Timestamp: build started"
bazel_ci build --nobuild //...
echo "Timestamp: build-deps fetched"
bazel_ci build //...
echo "Timestamp: build done"
bazel_ci test --test_output=errors //...
echo "Timestamp: test done"
# Some of the tests below pull Docker images from the repository. We need to
# make sure they are pushed and provide an access token.
gcloud auth configure-docker --quiet
REGISTRY="gcr.io/robco-integration-test"
TAG="latest" bazel_ci run \
//src/app_charts:push "${REGISTRY}"
# We're running into timeouts at CI and also don't see the actual failure
# reasons. Disable the test for now until someone has time and ideas how to
# resurrect it.
#
# set +o xtrace # Don't put the access token in the logs.
# ACCESS_TOKEN="$(gcloud auth application-default print-access-token)"
# Note: --strategy=TestRunner=standalone means that the tests are run locally
# and not on a remote worker (which does not have the Docker environment).
# bazel_ci test \
# --flaky_test_attempts 3 \
# --test_env ACCESS_TOKEN="${ACCESS_TOKEN}" \
# --test_env REGISTRY="${REGISTRY}" \
# --test_tag_filters="requires-docker" \
# --test_output=errors \
# --strategy=TestRunner=standalone //src/go/tests/apps:go_default_test
#
# set -o xtrace
echo "Timestamp: presubmit.sh done"
================================================
FILE: .github/ci/release_binary.sh
================================================
#!/bin/bash
set -euo pipefail
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
# shellcheck source=ci/common.sh
source "${DIR}/common.sh"
gcloud auth configure-docker --quiet
# Set defaults used for the release, they can only be overriden when testing
# manually.
GCP_BUCKET=${GCP_BUCKET:-"cloud-robotics-releases"}
VERSION=${VERSION:-"0.1.0"}
SHA=$(git rev-parse --short "$FULL_SHA")
RELEASE_NAME="v$VERSION-$SHA"
# TAG is a global variable that is used in the container push rules.
export TAG="crc-${VERSION}-${SHA}"
LABELS=${LABELS:-"latest crc-${VERSION}/crc-${VERSION}+latest"}
# Get the last release. We only create a new release if the main branch has moved since
# as trying to re-create an existing release is an error.
output=$(curl --fail-with-body -sS \
-H "Accept: application/vnd.github+json" \
-H "Authorization: token $GITHUB_TOKEN" \
https://api.github.com/repos/$REPO/releases/latest)
PREVIOUS_RELEASE_NAME="$(jq -r '.tag_name' <<< $output)"
if [ "$RELEASE_NAME" = "$PREVIOUS_RELEASE_NAME" ]; then
echo "Release $RELEASE_NAME already exists. Nothing more to do."
exit 0
else
echo "Previous release is $PREVIOUS_RELEASE_NAME"
fi
CLOUD_ROBOTICS_CONTAINER_REGISTRY="gcr.io/cloud-robotics-releases"
# DOCKER_TAG is a global variable that is used in release_binary.
DOCKER_TAG=${DOCKER_TAG:-"crc-${VERSION}-${SHA}"}
release_binary "${GCP_BUCKET}" "crc-${VERSION}/crc-${VERSION}+${SHA}" ${LABELS}
# Generate release notes comparing against the previous release.
output=$(curl --fail-with-body -sS \
-X POST \
-H "Accept: application/vnd.github+json" \
-H "Authorization: token $GITHUB_TOKEN" \
https://api.github.com/repos/$REPO/releases/generate-notes \
-d '{"tag_name":"'$RELEASE_NAME'","previous_tag_name":"'$PREVIOUS_RELEASE_NAME'"}')
# Code newlines as literal \n and escape double quotes to generate valid JSON.
BODY="$(jq -r '.body' <<< $output | awk '{printf "%s\\n", $0}' | sed 's/"/\\"/g')"
echo "Generated release notes for $RELEASE_NAME"
# Create the release on GitHub.
curl --fail-with-body -sS \
-X POST \
-H "Accept: application/vnd.github+json" \
-H "Authorization: token $GITHUB_TOKEN" \
https://api.github.com/repos/$REPO/releases \
--data-binary @- << EOF
{
"tag_name": "$RELEASE_NAME",
"name": "$RELEASE_NAME",
"body": "$BODY"
}
EOF
================================================
FILE: .github/dependabot.yml
================================================
version: 2
updates:
- package-ecosystem: "gomod"
directory: "/src/"
schedule:
interval: "weekly"
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
- package-ecosystem: "bazel"
directory: "/"
schedule:
interval: "weekly"
================================================
FILE: .github/workflows/check-bazel.yml
================================================
name: Check Bazel
on:
workflow_call:
permissions:
contents: read
id-token: write
jobs:
check-bazel:
runs-on: ubuntu-22.04
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # tag=v6.0.2
- name: Auth
uses: google-github-actions/auth@7c6bc770dae815cd3e89ee6cdf493a5fab2cc093 # tag=v3.0.0
with:
create_credentials_file: true # also sets GOOGLE_APPLICATION_CREDENTIALS
service_account: "github-automation-bot@gha-crc-dev.iam.gserviceaccount.com"
workload_identity_provider: "projects/1043719249528/locations/global/workloadIdentityPools/github-automation/providers/crc-dev"
- name: Print error on auth fail
if: failure()
run: |
echo >&2 "This PR appears to be from a fork or authored by a non-org member, rather than from the primary repo."
echo >&2 "This means it can't run the presubmit, which requires access to GCR."
echo >&2 "If you are a project member, please push your branch to github.com/googlecloudrobotics/core instead."
exit 1
- name: Run .github/ci/presubmit.sh
run: ./.github/ci/presubmit.sh
- name: Get bazel server logs
if: success() || failure()
run: cat ~/.cache/bazel/_bazel_*/*/java.log || true
================================================
FILE: .github/workflows/postsubmit.yml
================================================
name: Postsubmit
on:
schedule:
- cron: "0 4 * * *" # Once a day at 4am.
# Manual runs through Actions tab in the UI
workflow_dispatch:
inputs:
force-binary-release:
description: >-
force-binary-release: Set to non-empty when running from main to
create a binary release that can be used by 'Create release'.
permissions:
contents: read
id-token: write
concurrency:
group: integration_test
cancel-in-progress: true
jobs:
call-bazel:
uses: ./.github/workflows/check-bazel.yml
integration-test:
runs-on: ubuntu-22.04
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # tag=v6.0.2
- name: Auth
uses: google-github-actions/auth@7c6bc770dae815cd3e89ee6cdf493a5fab2cc093 # tag=v3.0.0
with:
create_credentials_file: true # also sets GOOGLE_APPLICATION_CREDENTIALS
service_account: "github-automation-bot@gha-crc-dev.iam.gserviceaccount.com"
workload_identity_provider: "projects/1043719249528/locations/global/workloadIdentityPools/github-automation/providers/crc-dev"
- name: Run integration_test.sh on Cloud Build
env:
MANUAL_RUN: "${{ github.event_name == 'workflow_dispatch' && inputs.force-binary-release == '' }}"
run: |
gcloud builds submit \
--project robco-integration-test \
--region europe-west1 \
--config .github/ci/integration_test_cloudbuild.yaml \
--substitutions _GITHUB_SHA=${GITHUB_SHA},_MANUAL_RUN=${MANUAL_RUN}
================================================
FILE: .github/workflows/presubmit.yml
================================================
name: Presubmit
on:
pull_request:
branches: ["main"]
workflow_dispatch:
permissions:
contents: read
id-token: write
pull-requests: read
# Cancel previous runs if a new one is started.
concurrency:
group: ${{ github.ref }}
cancel-in-progress: true
jobs:
setup-presubmit:
runs-on: ubuntu-22.04
outputs:
presubmit_digest: ${{ steps.pr-digest.outputs.digest }}
presubmit_status: ${{ steps.status.outputs.status }}
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # tag=v6.0.2
- name: Get PR digest
id: pr-digest
env:
GH_TOKEN: ${{ github.token }}
run: |
set -euo pipefail
# Get the list of changed files in the PR.
gh pr view ${{github.event.number}} --json files -q '.files[].path' > /tmp/changed_files.txt
# Create a tarball of the changed files and compute its SHA256.
# --ignore-failed-read to not fail on deleted files.
tar -cvf /tmp/changed_files.tar \
--owner=root --group=root --numeric-owner --mtime="2010-01-01" --sort=name \
-T /tmp/changed_files.txt \
--ignore-failed-read
digest=$(cat /tmp/changed_files.txt /tmp/changed_files.tar | sha256sum | cut -d " " -f1)
echo "digest=$digest" >> $GITHUB_OUTPUT
- uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: ~/PRESUBMITS_SUCCEEDED
key: PRESUBMITS_SUCCEEDED-${{ steps.pr-digest.outputs.digest }}
- name: Check for previous runs
id: status
run: |
if [ -f ~/PRESUBMITS_SUCCEEDED ]; then
echo "status=success" >> $GITHUB_OUTPUT
fi
call-check-bazel:
needs: [setup-presubmit]
if: ${{ needs.setup-presubmit.outputs.presubmit_status != 'success' }}
uses: ./.github/workflows/check-bazel.yml
presubmits-ok:
needs: [setup-presubmit, call-check-bazel]
runs-on: ubuntu-22.04
# To ensure this job always runs even if the "heavy" jobs were skipped.
# This allows us to guard merging on this check in Branch Protection.
if: ${{ always() }}
steps:
- name: Fail if tests failed
# always() because GitHub requires a status macro to be included or else this gets skipped.
# https://github.com/actions/runner/issues/491#issuecomment-850884422
if: ${{ always() && (contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled')) }}
run: exit 1
- name: Create presubmits succeeded marker
run: touch ~/PRESUBMITS_SUCCEEDED
- uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: ~/PRESUBMITS_SUCCEEDED
key: PRESUBMITS_SUCCEEDED-${{ needs.setup-presubmit.outputs.presubmit_digest }}
================================================
FILE: .github/workflows/release.yml
================================================
name: Create release
on:
schedule:
- cron: "0 5 * * *" # Once a day at 5am.
# Manual runs through Actions tab in the UI
workflow_dispatch:
permissions:
actions: read
contents: write
id-token: write
pull-requests: read
# Cancel previous runs if a new one is started.
concurrency:
group: ${{ github.ref }}
cancel-in-progress: true
jobs:
create_release:
runs-on: ubuntu-22.04
steps:
# Check out repo at latest green postsubmit commit on the main branch.
- name: Get latest passing commit
id: latest-green
env:
REPO: ${{ github.repository }}
run: |
set -euo pipefail
output=$(curl --fail-with-body -sS \
-H "Accept: application/vnd.github+json" \
"https://api.github.com/repos/$REPO/actions/workflows/postsubmit.yml/runs?per_page=1&branch=main&event=schedule&status=success")
repo_id=$(jq -r '.workflow_runs[0].head_repository.id' <<< $output)
if [[ "${repo_id}" != "${{ github.repository_id }}" ]] ; then
echo >&2 "Unexpected head repository ID: ${repo_id} - check postsubmit.yml configuration"
exit 1
fi
sha=$(jq -r '.workflow_runs[0].head_sha' <<< $output)
echo "latest_green=$sha" >> $GITHUB_OUTPUT
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # tag=v6.0.2
with:
ref: ${{ steps.latest-green.outputs.latest_green }}
- name: Auth
uses: google-github-actions/auth@7c6bc770dae815cd3e89ee6cdf493a5fab2cc093 # tag=v3.0.0
with:
create_credentials_file: true # also sets GOOGLE_APPLICATION_CREDENTIALS
service_account: "github-automation-bot@gha-crc-prod.iam.gserviceaccount.com"
workload_identity_provider: "projects/695270090783/locations/global/workloadIdentityPools/github-automation/providers/crc-prod"
- name: "Set up gcloud"
uses: "google-github-actions/setup-gcloud@aa5489c8933f4cc7a4f7d45035b3b1440c9c10db" # tag=v3.0.1
with:
skip_install: true
- name: Deploy Navtest on Cloud Build
run: |
gcloud builds submit \
--project robco-navtest \
--config .github/ci/deploy_navtest_cloudbuild.yaml
# Now we are ready to create the release.
- name: Run release_binary.sh
env:
REPO: ${{ github.repository }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
FULL_SHA: ${{ steps.latest-green.outputs.latest_green }}
run: ./.github/ci/release_binary.sh
================================================
FILE: .gitignore
================================================
.cache/
*.pyc
env/
# auto-generated by scripts/backport_kubeadm.sh
nsenter
# IntelliJ files
.project/
# Bazel
bazel-*
buildprofile.out
buildprofile.out.html
================================================
FILE: .pep8
================================================
[pep8]
aggressive=2
indent-size=2
max-line-length=80
================================================
FILE: BUILD.bazel
================================================
# Description:
# Root BUILD file for cloud-robotics
load("@bazel_gazelle//:def.bzl", "gazelle")
package(default_visibility = ["//visibility:public"])
exports_files([
"config.sh.tmpl",
"deploy.sh",
])
# Gazelle uses this to build importpath attributes.
# gazelle:prefix github.com/googlecloudrobotics/core
# Gazelle is used to generate BUILD.bazel files for WORKSPACE dependencies
# running this manually via "bazel run //:gazelle" will regenerate BUILD.bazel files that
# contain go-rules.
gazelle(
name = "gazelle",
)
# Libraries are named go_default_library, tests are named go_default_test.
# gazelle:go_naming_convention go_default_library
# We ignore the build files generated by bazel-deps as it doesn't use buildifer.
# gazelle:exclude third_party
# Also ignore the Go sources downloaded by src/go/deps.sh.
# gazelle:exclude src/.gopath
# Don't created build files for these examples
# gazelle:exclude docs/how-to/examples/greeter-service/proto/
================================================
FILE: CONTRIBUTING.md
================================================
# How to Contribute
We'd love to accept your patches and contributions to this project. There are
just a few small guidelines you need to follow.
## Contributor License Agreement
Contributions to this project must be accompanied by a Contributor License
Agreement. You (or your employer) retain the copyright to your contribution;
this simply gives us permission to use and redistribute your contributions as
part of the project. Head over to <https://cla.developers.google.com/> to see
your current agreements on file or to sign a new one.
You generally only need to submit a CLA once, so if you've already submitted one
(even if it was for a different project), you probably don't need to do it
again.
## Code reviews
All submissions, including submissions by project members, require review. We
use GitHub pull requests for this purpose. Consult
[GitHub Help](https://help.github.com/articles/about-pull-requests/) for more
information on using pull requests.
## Code formatting
We have a pre-commit hook to check code formatting, which you can install with:
```
ln -s ../../scripts/pre-commit .git/hooks/
```
It depends on external tools for formatting, which you may be prompted to
install when it first runs.
## Community Guidelines
This project follows
[Google's Open Source Community Guidelines](https://opensource.google.com/conduct/).
================================================
FILE: LICENSE
================================================
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
================================================
FILE: METADATA
================================================
name: "Cloud Robotics"
third_party {
# https://nvd.nist.gov/products/cpe/search
security {
tag: "NVD-CPE2.3:cpe:/a:grafana:grafana:10.3.1"
tag: "NVD-CPE2.3:cpe:/a:kubernetes:ingress-nginx:1.8.4"
tag: "NVD-CPE2.3:cpe:/a:oauth2_proxy_project:oauth2_proxy:7.5.1"
tag: "NVD-CPE2.3:cpe:/a:prometheus:prometheus:2.49.1"
}
}
================================================
FILE: MODULE.bazel
================================================
bazel_dep(name = "aspect_bazel_lib", version = "2.21.2")
bazel_dep(name = "bazel_skylib", version = "1.8.1")
bazel_dep(name = "platforms", version = "1.0.0")
bazel_dep(name = "protobuf", version = "29.0", repo_name = "com_google_protobuf")
bazel_dep(name = "rules_oci", version = "2.0.1")
bazel_dep(name = "rules_pkg", version = "1.0.1")
bazel_dep(name = "rules_shell", version = "0.4.1")
# -- bazel_dep definitions -- #
non_module_deps = use_extension("//:non_module_deps.bzl", "non_module_deps")
use_repo(non_module_deps, "kubernetes_helm")
use_repo(non_module_deps, "kubernetes_helm3")
use_repo(non_module_deps, "hashicorp_terraform")
use_repo(non_module_deps, "com_github_kubernetes_sigs_application")
use_repo(non_module_deps, "ingress-nginx")
# End of extension `non_module_deps`
#######
# C++ #
#######
bazel_dep(name = "toolchains_llvm", version = "1.7.0")
# Inspect supported toolchains at https://github.com/bazel-contrib/toolchains_llvm/blob/master/toolchain/internal/llvm_distributions.bzl
llvm = use_extension("@toolchains_llvm//toolchain/extensions:llvm.bzl", "llvm")
llvm.toolchain(
llvm_version = "18.1.4",
)
use_repo(non_module_deps, "com_googleapis_storage_chrome_linux_amd64_sysroot")
llvm.sysroot(
label = "@com_googleapis_storage_chrome_linux_amd64_sysroot//:all_files",
targets = ["linux-x86_64"],
)
use_repo(llvm, "llvm_toolchain")
register_toolchains("@llvm_toolchain//:all")
bazel_dep(name = "rules_cc", version = "0.1.5")
######
# Go #
######
bazel_dep(name = "rules_go", version = "0.59.0", repo_name = "io_bazel_rules_go")
go_sdk = use_extension("@io_bazel_rules_go//go:extensions.bzl", "go_sdk")
go_sdk.download(version = "1.25.4")
bazel_dep(name = "gazelle", version = "0.47.0", repo_name = "bazel_gazelle")
go_deps = use_extension("@bazel_gazelle//:extensions.bzl", "go_deps")
go_deps.from_file(go_mod = "//src:go.mod")
use_repo(
go_deps,
"com_github_cenkalti_backoff",
"com_github_form3tech_oss_jwt_go",
"com_github_fsnotify_fsnotify",
"com_github_getlantern_httptest",
"com_github_golang_glog",
"com_github_golang_mock",
"com_github_google_go_cmp",
"com_github_google_nftables",
"com_github_googlecloudrobotics_ilog",
"com_github_jaypipes_ghw",
"com_github_jaypipes_pcidb",
"com_github_motemen_go_loghttp",
"com_github_onsi_gomega",
"com_github_pkg_errors",
"com_github_prometheus_client_golang",
"com_github_spf13_cobra",
"com_github_spf13_pflag",
"com_google_cloud_go_compute_metadata",
"com_google_cloud_go_storage",
"in_gopkg_h2non_gock_v1",
"io_k8s_api",
"io_k8s_apiextensions_apiserver",
"io_k8s_apimachinery",
"io_k8s_cli_runtime",
"io_k8s_client_go",
"io_k8s_helm",
"io_k8s_klog",
"io_k8s_klog_v2",
"io_k8s_sigs_controller_runtime",
"io_k8s_sigs_kind",
"io_k8s_sigs_yaml",
"io_opencensus_go",
"io_opencensus_go_contrib_exporter_prometheus",
"io_opencensus_go_contrib_exporter_stackdriver",
"org_golang_google_api",
"org_golang_google_grpc",
"org_golang_google_protobuf",
"org_golang_x_crypto",
"org_golang_x_net",
"org_golang_x_oauth2",
"org_golang_x_sync",
)
#######
# OCI #
#######
oci = use_extension("@rules_oci//oci:extensions.bzl", "oci")
# gcloud container images describe gcr.io/distroless/base:latest --format='value(image_summary.digest)'
oci.pull(
name = "distroless_base",
digest = "sha256:b31a6e02605827e77b7ebb82a0ac9669ec51091edd62c2c076175e05556f4ab9",
image = "gcr.io/distroless/base",
platforms = ["linux/amd64"],
)
# gcloud container images describe gcr.io/distroless/cc:latest --format='value(image_summary.digest)'
oci.pull(
name = "distroless_cc",
digest = "sha256:8aad707f96620ee89e27febef51b01c6ff244277a3560fcfcfbe68633ef09193",
image = "gcr.io/distroless/cc",
platforms = ["linux/amd64"],
)
oci.pull(
name = "iptables_base",
digest = "sha256:656e45c00083359107b1d6ae0411ff3894ba23011a8533e229937a71be84e063",
image = "gcr.io/google-containers/debian-iptables",
platforms = ["linux/amd64"],
)
use_repo(
oci,
"distroless_base",
"distroless_base_linux_amd64",
"distroless_cc",
"distroless_cc_linux_amd64",
"iptables_base",
"iptables_base_linux_amd64",
)
================================================
FILE: README.md
================================================
# Cloud Robotics Core
Google's Cloud Robotics Core is an open source platform that provides
infrastructure essential to building and running robotics solutions for business
automation. Cloud Robotics Core makes managing robot fleets easy for developers,
integrators, and operators. It enables:
* packaging and distribution of applications
* secure, bidirectional robot-cloud communication
* easy access to Google Cloud services such as ML, logging, and monitoring.

Cloud Robotics Core is open source and pre-alpha. Support is currently limited
to a small set of early access partners. We will gladly accept contributions
and feedback, but we are making no stability or support guarantees at this
point in time.
# Documentation
Documentation of the platform and related How-to guides can be found at: https://googlecloudrobotics.github.io/core/
# Get Involved
If you want to get involved, please refer to [CONTRIBUTING.md](CONTRIBUTING.md),
reach out to [cloud-robotics-discuss@googlegroups.com](https://groups.google.com/forum/#!forum/cloud-robotics-discuss)
or ask Stack Overflow questions with [#google-cloud-robotics](https://stackoverflow.com/questions/tagged/google-cloud-robotics).
# Source Code
Most interesting bits are under `src`:
* app_charts: contains kubernetes resources for the core platform and apps
* bootstrap: provisioning for the cloud (terraform) and the robot (debian package)
* go/: the code that goes into images referenced from `app_charts`
The root directory contains a `deploy.sh` script for building and installing the software. More
details on that are in the [building from sources](how-to/deploy-from-sources) guide.
================================================
FILE: bazel/BUILD.bazel
================================================
exports_files([
"app.bzl",
"app_chart.bzl",
"container_push.bzl",
"repositories.bzl",
])
platform(
name = "linux_x86_64",
constraint_values = [
"@platforms//os:linux",
"@platforms//cpu:x86_64",
"@bazel_tools//tools/cpp:clang",
],
exec_properties = {
"container-image": "docker://gcr.io/cloud-robotics-releases/bazel-rbe-executor@sha256:3ee043e7a322caaff8c9edaa302373deb80e67ad6e42ae35d34b8f3597b8995e",
"OSFamily": "Linux",
},
parents = ["@local_config_platform//:host"],
)
================================================
FILE: bazel/BUILD.sysroot
================================================
filegroup(
name = "all_files",
srcs = glob(
[
"lib/x86_64-linux-gnu/ld*",
"lib/x86_64-linux-gnu/libc*",
"lib/x86_64-linux-gnu/libdl*",
"lib/x86_64-linux-gnu/libgcc*",
"lib/x86_64-linux-gnu/libm*",
"lib/x86_64-linux-gnu/libpthread*",
"lib/x86_64-linux-gnu/librt*",
"lib/x86_64-linux-gnu/libutil*",
"lib64/**",
"usr/include/*.h",
"usr/include/arpa/**",
"usr/include/asm-generic/**",
"usr/include/c++/**",
"usr/include/linux/**",
"usr/include/net/**",
"usr/include/netinet/**",
"usr/include/rpc/**",
"usr/include/sys/**",
"usr/include/x86_64-linux-gnu/**",
"usr/lib/gcc/**",
"usr/lib/x86_64-linux-gnu/*crt*.o",
"usr/lib/x86_64-linux-gnu/libc_nonshared.a",
"usr/lib/x86_64-linux-gnu/libc.a",
"usr/lib/x86_64-linux-gnu/libc.so",
"usr/lib/x86_64-linux-gnu/libdl*",
"usr/lib/x86_64-linux-gnu/libm*",
"usr/lib/x86_64-linux-gnu/libpthread*",
"usr/lib/x86_64-linux-gnu/libresolv.so",
"usr/lib/x86_64-linux-gnu/librt*",
"usr/lib/x86_64-linux-gnu/libutil*",
],
),
visibility = ["//visibility:public"],
)
================================================
FILE: bazel/app.bzl
================================================
load("//bazel/build_rules/app_chart:run_parallel.bzl", "run_parallel")
def app(name, charts, visibility = None):
"""Macro for a standard Cloud Robotics app.
This macro establishes two subrules for app name "foo":
- :foo.push pushes the Docker images for the app.
- :foo.yaml is a YAML file with the app CR that you need to push to
Kubernetes. Use k8s_object to push it, or compile it into a Helm chart.
Args:
name: string. Name of the app.
charts: list of targets. Helm charts for this app.
visibility: Visibility.
"""
pkg = Label("{}//{}".format(native.repository_name(), native.package_name()))
chart_labels = [pkg.relative(c) for c in charts]
run_parallel(
name = name + ".push",
targets = ["//{}:{}.push".format(c.package, c.name) for c in chart_labels],
visibility = visibility,
)
native.genrule(
# we name this differently than the file we produce to silence:
# target 'xxx.yaml' is both a rule and a file; please choose another name for the rule
name = name + ".manifest",
srcs = [
"//{}:{}.snippet-yaml".format(c.package, c.name)
for c in chart_labels
],
outs = [name + ".yaml"],
cmd = """cat - $(SRCS) > $@ <<EOF
apiVersion: apps.cloudrobotics.com/v1alpha1
kind: App
metadata:
name: {name}-dev
spec:
components:
EOF
""".format(name = name),
visibility = visibility,
)
================================================
FILE: bazel/app_chart.bzl
================================================
load("//bazel:build_rules/helm_chart.bzl", "helm_chart")
load("//bazel/build_rules/app_chart:cache_gcr_credentials.bzl", "cache_gcr_credentials")
load("//bazel/build_rules/app_chart:push_all.bzl", "push_all")
def _impl(ctx):
chart_yaml = ctx.actions.declare_file(ctx.label.name + "-chart.yaml")
ctx.actions.expand_template(
template = ctx.file._chart_yaml_template,
output = chart_yaml,
substitutions = {"${name}": ctx.label.name, "${version}": "0.0.1"},
)
values_yaml = ctx.actions.declare_file(ctx.label.name + "-values.yaml")
source_digests = []
cmds = [
"cat {} - > {} <<EOF".format(ctx.file.values.path, values_yaml.path),
"### Generated by app_chart ###",
"images:",
]
jq_bin = ctx.toolchains["@aspect_bazel_lib//lib:jq_toolchain_type"].jqinfo.bin
images = ctx.attr.images or {}
for key, value in images.items():
key_files = key[DefaultInfo].files
for file in key_files.to_list():
image_dir = file
# keep the leading '/' since helm charts prepend the registry without one
cmds.append(" {nick}: /{image}@$({jq} -r '.manifests[0].digest' {digest}/index.json)".format(
nick = value.replace("-", "_"),
image = value,
digest = image_dir.path,
jq = jq_bin.path,
))
source_digests.append(image_dir)
cmds.append("EOF")
ctx.actions.run_shell(
tools = [jq_bin],
outputs = [values_yaml],
inputs = ctx.files.values + source_digests,
command = "\n".join(cmds),
toolchain = None,
)
helm_chart(
ctx,
name = ctx.label.name,
chart = chart_yaml,
values = values_yaml,
# TODO(b/72936439): This is currently unused and fixed to 0.0.1.
version = "0.0.1",
templates = ctx.files.templates,
files = ctx.files.files,
helm = ctx.file._helm,
out = ctx.outputs.chart,
)
return [DefaultInfo(
runfiles = ctx.runfiles(files = [ctx.outputs.chart]),
files = depset([ctx.outputs.chart]),
)]
_app_chart_backend = rule(
implementation = _impl,
attrs = {
"chart": attr.string(
doc = "the chart name (robot/cloud/cloud-per-robot)",
mandatory = True,
),
"values": attr.label(
allow_single_file = True,
doc = "The values.yaml file.",
),
"templates": attr.label_list(
allow_empty = True,
allow_files = True,
default = [],
doc = "Files for the chart's templates/ directory.",
),
"files": attr.label_list(
allow_empty = True,
allow_files = True,
default = [],
doc = "Extra non-template files for the chart's files/ directory.",
),
"images": attr.label_keyed_string_dict(
allow_empty = True,
doc = "Images referenced by the chart.",
),
"_chart_yaml_template": attr.label(
default = Label("//bazel/build_rules/app_chart:Chart.yaml.template"),
allow_single_file = True,
),
"_helm": attr.label(
default = Label("@kubernetes_helm//:helm"),
allow_single_file = True,
),
},
outputs = {
"chart": "%{name}-0.0.1.tgz",
},
toolchains = ["@aspect_bazel_lib//lib:jq_toolchain_type"],
)
def app_chart(
name,
values = None,
extra_templates = None,
files = None,
images = None,
visibility = None):
"""Macro for a standard Cloud Robotics helm chart.
This macro establishes two subrules for chart name "foo-cloud":
- :foo-cloud.push pushes the Docker images for the chart (if relevant).
- :foo-cloud.snippet-yaml is a snippet of YAML defining the chart, which is
used by app() to generate an App CR containing multiple inline
charts.
Args:
name: string. Must be in the format {app}-{chart}, where chart is
robot, cloud, or cloud-per-robot.
values: file. The values.yaml file.
extra_templates: list of files. Extra files for the chart's templates/ directory.
files: list of files. Extra non-template files for the chart's files/ directory.
images: dict. Images referenced by the chart.
visibility: Visibility.
"""
_, chart = name.rsplit("-", 1)
if name.endswith("cloud-per-robot"):
chart = "cloud-per-robot"
if not values:
if chart == "cloud":
values = Label("//bazel/build_rules/app_chart:values-cloud.yaml")
else:
values = Label("//bazel/build_rules/app_chart:values-robot.yaml")
# We have a dict of string:target, but bazel rules only support target:string.
reversed_images = {}
if images:
for k, v in images.items():
reversed_images[v] = k
_app_chart_backend(
name = name,
chart = chart,
values = values,
templates = native.glob([chart + "/*.yaml"], allow_empty = True) + (extra_templates or []),
files = files,
images = reversed_images,
visibility = visibility,
)
push_all(
name = name + ".push-all-containers",
images = images,
)
cache_gcr_credentials(
name = name + ".push",
target = name + ".push-all-containers",
visibility = visibility,
)
native.genrule(
name = name + ".snippet-yaml",
srcs = [name],
outs = [name + ".snippet.yaml"],
cmd = """cat <<EOF > $@
{target}:
inline: $$(base64 -w 0 $<)
EOF
""".format(name = name, target = chart),
)
================================================
FILE: bazel/build_rules/app_chart/BUILD.bazel
================================================
exports_files([
"cache_gcr_credentials.sh.tpl",
"Chart.yaml.template",
"push_all.sh.tpl",
"run_parallel.sh.tpl",
"values-cloud.yaml",
"values-robot.yaml",
])
================================================
FILE: bazel/build_rules/app_chart/Chart.yaml.template
================================================
apiVersion: v1
name: ${name}
version: ${version}
# Linter expects an icon.
icon: https://google.com/icon.png
================================================
FILE: bazel/build_rules/app_chart/cache_gcr_credentials.bzl
================================================
def _get_runfile_path(ctx, f):
"""Return the runfiles relative path of f."""
if ctx.workspace_name:
return "${RUNFILES}/" + ctx.workspace_name + "/" + f.short_path
else:
return "${RUNFILES}/" + f.short_path
def _impl(ctx):
runfiles = ctx.attr._sh_tpl.default_runfiles.files.to_list()
runfiles.append(ctx.attr.target.files_to_run.executable)
runfiles.extend(ctx.attr.target.default_runfiles.files.to_list())
variables = "PYTHON_RUNFILES=\"${RUNFILES}\" "
ctx.actions.expand_template(
template = ctx.file._sh_tpl,
substitutions = {
"%{gcr_registry}": ctx.attr.gcr_registry,
"%{command}": variables + _get_runfile_path(ctx, ctx.attr.target.files_to_run.executable),
},
output = ctx.outputs.executable,
is_executable = True,
)
return [DefaultInfo(runfiles = ctx.runfiles(files = runfiles))]
cache_gcr_credentials = rule(
attrs = {
"target": attr.label(
mandatory = True,
),
"gcr_registry": attr.string(
default = "gcr.io",
doc = "If set, credentials for this GCR registry's domain will be precached",
),
"_sh_tpl": attr.label(
default = Label("//bazel/build_rules/app_chart:cache_gcr_credentials.sh.tpl"),
allow_single_file = True,
),
},
executable = True,
implementation = _impl,
)
"""Cache gcr credentials before running a command.
This rule executes docker-credential-gcloud before running the command, and
replaces the binary with a helper that is safe for concurrent execution. Works
around around https://github.com/bazelbuild/rules_docker/issues/511.
Args:
target: A target that can be run with "bazel run".
gcr_registry: string. A GCR Docker registry (gcr.io/myproject).
"""
================================================
FILE: bazel/build_rules/app_chart/cache_gcr_credentials.sh.tpl
================================================
#!/usr/bin/env bash
set -eu
function guess_runfiles() {
pushd ${BASH_SOURCE[0]}.runfiles > /dev/null 2>&1
pwd
popd > /dev/null 2>&1
}
RUNFILES="${PYTHON_RUNFILES:-$(guess_runfiles)}"
# app() uses run_parallel() to push images to GCR, which relies on
# gcloud to get credentials. That, however, has a race condition:
# https://github.com/google/containerregistry/issues/115
# As such, we cache credentials and create a script that prints them to
# replace the racy credential helper. This script is added to to the start of
# PATH. This is harmless if run_parallel() is being used for something else.
# It also saves ~5s of CPU time.
tmp_bin=$(mktemp --tmpdir= -d deploy-XXXXXXXX-bin)
export PATH="${tmp_bin}:${PATH}"
function rm_tmp_bin {
rm -r "${tmp_bin}"
}
trap rm_tmp_bin EXIT
credential_script=$tmp_bin/docker-credential-gcloud
credential_file=$tmp_bin/docker-credential-gcloud.json
gcp_registry=$(echo "%{gcr_registry}" | cut -d'/' -f 1)
docker-credential-gcloud get <<<"https://${gcp_registry}" > "${credential_file}"
cat > "${credential_script}" << EOF
#!/bin/bash
cat "${credential_file}"
EOF
chmod +x "${credential_script}"
%{command} "$@"
================================================
FILE: bazel/build_rules/app_chart/push_all.bzl
================================================
load("//bazel:container_push.bzl", "container_push")
def _get_runfile_path(ctx, f):
"""Return the runfiles relative path of f."""
if ctx.workspace_name:
return "${RUNFILES}/" + ctx.workspace_name + "/" + f.short_path
else:
return "${RUNFILES}/" + f.short_path
def _impl(ctx):
runfiles = ctx.attr._sh_tpl.default_runfiles.files.to_list()
for target in ctx.attr.push_targets:
runfiles.append(target.files_to_run.executable)
runfiles.extend(target.default_runfiles.files.to_list())
ctx.actions.expand_template(
template = ctx.file._sh_tpl,
substitutions = {
"%{commands}": "\n".join(
[
"if [[ -z \"${TAG:-}\" ]]; then echo >&2 \"$0: TAG environment variable must be set when pushing images.\"; exit 1; fi",
] + [
"async {command} --repository=\"${{CONTAINER_REGISTRY}}/{repository}\" --tag=\"${{TAG}}\"".format(
command = _get_runfile_path(ctx, target.files_to_run.executable),
repository = repository,
)
for target, repository in zip(ctx.attr.push_targets, ctx.attr.images.keys())
],
),
},
output = ctx.outputs.executable,
is_executable = True,
)
return [DefaultInfo(runfiles = ctx.runfiles(files = runfiles))]
_push_all = rule(
attrs = {
# Implicit dependencies.
"push_targets": attr.label_list(
allow_files = True,
),
"images": attr.string_dict(
default = {},
),
"_sh_tpl": attr.label(
default = Label("//bazel/build_rules/app_chart:push_all.sh.tpl"),
allow_single_file = True,
),
},
executable = True,
implementation = _impl,
)
def push_all(name, images = {}, **kwargs):
"""Creates a script to push several container images to a docker registry.
The registry has to be specified as parameter when invoking the script.
Args:
images: dict. Repository names as keys and images to be pushed as values.
"""
if "push_targets" in kwargs:
fail("reserved for internal use by push_all macro", attr = "push_targets")
images = images or {}
push_targets = []
for repository, image in images.items():
push_target = name + "." + repository + ".push"
push_targets.append(push_target)
container_push(
name = push_target,
image = image,
)
_push_all(name = name, images = images, push_targets = push_targets, **kwargs)
================================================
FILE: bazel/build_rules/app_chart/push_all.sh.tpl
================================================
#!/usr/bin/env bash
set -eu
if [[ "$#" -lt 1 ]]; then
echo "Usage: $0 <container-registry>"
exit 1
fi
CONTAINER_REGISTRY="$1"
function guess_runfiles() {
pushd ${BASH_SOURCE[0]}.runfiles > /dev/null 2>&1
pwd
popd > /dev/null 2>&1
}
RUNFILES="${PYTHON_RUNFILES:-$(guess_runfiles)}"
PIDS=()
function async() {
# Launch the command asynchronously and track its process id.
PYTHON_RUNFILES=${RUNFILES} "$@" &
PIDS+=($!)
}
%{commands}
if [[ "${#PIDS[@]}" = 0 ]]; then
# It is valid to generate this script without pushing any images.
# Bash before v4.4 considers an empty array an unbound variable and would
# choke on the for-loop below.
exit 0
fi
# Wait for all of the subprocesses, failing the script if any of them failed.
exitcode=0
for pid in "${PIDS[@]}"; do
wait ${pid} || exitcode=$?
done
exit $exitcode
================================================
FILE: bazel/build_rules/app_chart/run_parallel.bzl
================================================
def _get_runfile_path(ctx, f):
"""Return the runfiles relative path of f."""
if ctx.workspace_name:
return "${RUNFILES}/" + ctx.workspace_name + "/" + f.short_path
else:
return "${RUNFILES}/" + f.short_path
def _impl(ctx):
runfiles = ctx.attr._sh_tpl.default_runfiles.files.to_list()
for target in ctx.attr.targets:
runfiles.append(target.files_to_run.executable)
runfiles.extend(target.default_runfiles.files.to_list())
ctx.actions.expand_template(
template = ctx.file._sh_tpl,
substitutions = {
"%{commands}": "\n".join([
"async \"%s\" \"$@\"" % _get_runfile_path(ctx, command.files_to_run.executable)
for command in ctx.attr.targets
]),
},
output = ctx.outputs.executable,
is_executable = True,
)
return [DefaultInfo(runfiles = ctx.runfiles(files = runfiles))]
run_parallel = rule(
attrs = {
"targets": attr.label_list(
allow_empty = False,
mandatory = True,
),
"_sh_tpl": attr.label(
default = Label("//bazel/build_rules/app_chart:run_parallel.sh.tpl"),
allow_single_file = True,
),
},
executable = True,
implementation = _impl,
)
"""Run multiple targets in parallel.
This rule builds a "bazel run" target that runs a series of subtargets in
parallel. If a subtarget has errors, execution results in an error when all
subtargets have completed.
Args:
targets: A list of targets that can be run with "bazel run".
"""
================================================
FILE: bazel/build_rules/app_chart/run_parallel.sh.tpl
================================================
#!/usr/bin/env bash
set -eu
function guess_runfiles() {
pushd ${BASH_SOURCE[0]}.runfiles > /dev/null 2>&1
pwd
popd > /dev/null 2>&1
}
RUNFILES="${PYTHON_RUNFILES:-$(guess_runfiles)}"
PIDS=()
function async() {
# Launch the command asynchronously and track its process id.
PYTHON_RUNFILES=${RUNFILES} "$@" &
PIDS+=($!)
}
%{commands}
# Wait for all of the subprocesses, failing the script if any of them failed.
exitcode=0
for pid in "${PIDS[@]}"; do
wait ${pid} || exitcode=$?
done
exit $exitcode
================================================
FILE: bazel/build_rules/app_chart/values-cloud.yaml
================================================
domain: "example.com"
project: "my-gcp-project"
deploy_environment: "GCP"
registry: "gcr.io/my-gcp-project"
robots: []
region: example-gcp-region
# Token Vendor feature flags
use_tv_k8s_verbose: false
================================================
FILE: bazel/build_rules/app_chart/values-robot.yaml
================================================
domain: "example.com"
project: "my-gcp-project"
deploy_environment: "GCP"
registry: "gcr.io/my-gcp-project"
robot:
name: ""
================================================
FILE: bazel/build_rules/copy.bzl
================================================
"""Macros to help rearrange files."""
def copy_files(name, srcs, outdir, visibility = None):
"""Creates copies of files within a directory.
For each source file, a copy with the same basename is created in outdir. A
filegroup target <name> is created containing the new files.
Args:
srcs: list of files.
outdir: output directory.
visibility: handled through to the targets.
"""
outs = []
for src in srcs:
filename = src.split("/")[-1]
out = "%s/%s" % (outdir, filename)
native.genrule(
name = "%s_%s" % (name, filename),
srcs = [src],
outs = [out],
cmd = "cp $< $@",
visibility = visibility,
)
outs.append(out)
native.filegroup(
name = name,
srcs = outs,
visibility = visibility,
)
================================================
FILE: bazel/build_rules/helm_chart.bzl
================================================
def helm_chart(ctx, name, chart, files, templates, values, version, helm, out):
"""Starlark function that builds a helm chart.
Args:
name: string. Must match the name in Chart.yaml.
chart: file. The Chart.yaml file.
files: list of non-template files to put in files/.
templates: list of template files.
values: file. The values.yaml file.
version: string. Overwrites any version in Chart.yaml.
helm: file. The Helm tool.
out: file. The file that the chart is built to.
"""
cmd = """
mkdir {name} {name}/templates
cp {chart} {name}/Chart.yaml
cp {values} {name}/values.yaml
""".format(name = name, chart = chart.path, values = values.path)
if templates:
template_files = " ".join([t.path for t in templates])
# Use a single cp invocation to detect filename clashes.
cmd += "cp {templates} {name}/templates\n".format(name = name, templates = template_files)
if files:
cmd += "mkdir {name}/files\n".format(name = name)
files_locations = " ".join([f.path for f in files])
# Use a single cp invocation to detect filename clashes.
cmd += "cp {files} {name}/files\n".format(name = name, files = files_locations)
cmd += """
# Linter is too noisy, swallow its output when not failing
{helm} lint --strict {name} >/dev/null 2>&1 || \\
{helm} lint --strict {name}
{helm} package \\
--save=false --version={version} {name} \\
| (grep -v "Successfully packaged" || true)
mv $(basename {output}) {output}
rm -rf {name}""".format(name = name, version = version, helm = helm.path, output = out.path)
ctx.actions.run_shell(
inputs = [chart, values] + templates + (files or []),
tools = [helm],
outputs = [out],
command = cmd,
toolchain = None,
)
================================================
FILE: bazel/build_rules/helm_template.bzl
================================================
def helm_template(name, release_name, chart, values, namespace = None, helm_version = 2):
"""Locally expand a helm chart.
Args:
chart: build label, referencing the chart to expand.
values: label. File with expand-time values.
"""
tool = ""
cmd = ""
if helm_version == 2:
tool = "@kubernetes_helm//:helm"
cmd = "$(location {tool}) template --name {name} --namespace {namespace} --values $(location {values}) $(location {chart}) > $@".format(name = release_name, namespace = namespace or "default", chart = chart, tool = tool, values = values)
elif helm_version == 3:
tool = "@kubernetes_helm3//:helm"
cmd = "$(location {tool}) template {name} $(location {chart}) --namespace {namespace} --values $(location {values}) > $@".format(name = release_name, namespace = namespace or "default", chart = chart, tool = tool, values = values)
else:
fail("Unsupported helm version. Expected {2,3}, got ", helm_version)
native.genrule(
name = name,
srcs = [chart, values],
outs = [name + ".yaml"],
cmd = cmd,
tools = [tool],
)
================================================
FILE: bazel/container_push.bzl
================================================
load("@rules_oci//oci:defs.bzl", "oci_push")
def container_push(*args, **kwargs):
"""Creates a script to push a container image to a Docker registry. The
target name must be specified when invoking the push script."""
if "repository" in kwargs:
fail(
"Cannot set 'repository' attribute on container_push",
attr = "repository",
)
kwargs["repository"] = "IGNORE"
oci_push(*args, **kwargs)
================================================
FILE: bazel/debug_repository.bzl
================================================
"""Debug util for repository definitions."""
def debug_repository(repo, *fields):
"""debug_repository(repo) identifies which version of a repository has been
defined in the WORKSPACE by printing some of its fields. Example:
# at the bottom of the WORKSPACE file
load("//bazel:debug_repository.bzl", "debug_repository")
debug_repository("org_golang_x_net")
If needed, you can override the printed fields by passing additional parameters:
debug_repository("io_grpc_grpc_java", "patches", "urls")
"""
if len(fields) == 0:
fields = ["branch", "commit", "tag", "url", "urls"]
rule = native.existing_rule(repo)
if rule == None:
print(repo, "not found")
return
for f in fields:
if f in rule and len(rule[f]) > 0:
print(repo, f, rule[f])
================================================
FILE: config.sh.tmpl
================================================
#!/usr/bin/env bash
### Required settings ###
# Project ID of your Cloud Robotics GCP project. This project can be created
# for you as part of the Terraform setup, or it can be created and configured
# manually, then imported with `deploy.sh set-project` or `terraform import`.
GCP_PROJECT_ID=my-project
# GCP region and zone where resources should be created.
GCP_REGION=europe-west1
GCP_ZONE=europe-west1-c
### Optional settings ###
# The Docker registry all Cloud Robotics images are deployed to when installing
# from sources. It is ignored during binary installs.
# If unset, defaults to "gcr.io/${GCP_PROJECT_ID}"
#CLOUD_ROBOTICS_CONTAINER_REGISTRY=gcr.io/my-project
# A space-separated list of GCP alphanumeric project IDs for private image
# repositories. The installer will provision GCR access to these projects,
# both for the gke-node service account and for the robot service account.
#PRIVATE_DOCKER_PROJECTS="my-project my-other-project"
# A Google Group that should be a co-owner of the created GCP project.
#CLOUD_ROBOTICS_SHARED_OWNER_GROUP=my-group@googlegroups.com
# If you want to store your Terraform state in a GCS bucket, give a bucket name
# and a subdirectory of the bucket here. See
# https://www.terraform.io/docs/backends/types/gcs.html for docs.
#TERRAFORM_GCS_BUCKET="my-gcs-bucket"
#TERRAFORM_GCS_PREFIX="my/sub/directory"
# Symmetric cookie encryption key for the oauth2-proxy. Generate with:
# python -c 'import os,base64; print base64.urlsafe_b64encode(os.urandom(16))'
#CLOUD_ROBOTICS_COOKIE_SECRET=A_CyACoujODhfn2yDMy5tw==
# Oauth2 client ID and client secret from
# https://console.cloud.google.com/apis/credentials. If you leave these empty,
# you won't be able to log in with a browser (but CLI access will work fine).
#CLOUD_ROBOTICS_OAUTH2_CLIENT_ID=....apps.googleusercontent.com
#CLOUD_ROBOTICS_OAUTH2_CLIENT_SECRET=...
# Domain to be used for the ingress
# If unset, defaults to "www.endpoints.${GCP_PROJECT_ID}.cloud.goog"
#CLOUD_ROBOTICS_DOMAIN=www.example.com
# Enable google cloud robotics layer 2
APP_MANAGEMENT=true
# Enable google cloud robotics layer 1
ONPREM_FEDERATION=true
# Disable the secret manager integration by default
GKE_SECRET_MANAGER_PLUGIN=false
================================================
FILE: current_versions.txt
================================================
{
"cert-manager": "1.16.3",
"ingress-nginx": "1.8.4",
"oauth2-proxy": "7.5.1",
"stackdriver-logging-agent": "1.9.5"
}
================================================
FILE: deploy.sh
================================================
#!/bin/bash
#
# Copyright 2019 The Cloud Robotics 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.
# Manage a deployment
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
source "${DIR}/scripts/common.sh"
source "${DIR}/scripts/config.sh"
source "${DIR}/scripts/include-config.sh"
set -o pipefail -o errexit
PROJECT_NAME="cloud-robotics"
RUNFILES_ROOT="_main"
if is_source_install; then
# Not using bazel run to not clobber the bazel-bin dir
TERRAFORM="${DIR}/bazel-out/../../../external/+non_module_deps+hashicorp_terraform/terraform"
HELM_COMMAND="${DIR}/bazel-out/../../../external/+non_module_deps+kubernetes_helm/helm"
SYNK_COMMAND="${DIR}/bazel-bin/src/go/cmd/synk/synk_/synk"
else
TERRAFORM="${DIR}/bin/terraform"
HELM_COMMAND="${DIR}/bin/helm"
SYNK_COMMAND="${DIR}/bin/synk"
fi
TERRAFORM_DIR="${DIR}/src/bootstrap/cloud/terraform"
TERRAFORM_APPLY_FLAGS=${TERRAFORM_APPLY_FLAGS:- -auto-approve}
# utility functions
function include_config_and_defaults {
include_config "$1"
CLOUD_ROBOTICS_DOMAIN=${CLOUD_ROBOTICS_DOMAIN:-"www.endpoints.${GCP_PROJECT_ID}.cloud.goog"}
APP_MANAGEMENT=${APP_MANAGEMENT:-false}
ONPREM_FEDERATION=${ONPREM_FEDERATION:-true}
GKE_SECRET_MANAGER_PLUGIN=${GKE_SECRET_MANAGER_PLUGIN:-false}
# lets-encrypt is used as the default certificate provider for backwards compatibility purposes
CLOUD_ROBOTICS_CERTIFICATE_PROVIDER=${CLOUD_ROBOTICS_CERTIFICATE_PROVIDER:-lets-encrypt}
CLOUD_ROBOTICS_CERTIFICATE_SUBJECT_COMMON_NAME=${CLOUD_ROBOTICS_CERTIFICATE_SUBJECT_COMMON_NAME:-GCP_PROJECT_ID}
CLOUD_ROBOTICS_CERTIFICATE_SUBJECT_ORGANIZATION=${CLOUD_ROBOTICS_CERTIFICATE_SUBJECT_ORGANIZATION:-GCP_PROJECT_ID}
CLOUD_ROBOTICS_OWNER_EMAIL=${CLOUD_ROBOTICS_OWNER_EMAIL:-$(gcloud config get-value account)}
CLOUD_ROBOTICS_CTX=${CLOUD_ROBOTICS_CTX:-"gke_${GCP_PROJECT_ID}_${GCP_ZONE}_${PROJECT_NAME}"}
}
function update_config_var {
cloud_bucket="gs://${1}-cloud-robotics-config"
name="${2}"
value="${3}"
config_file="$(mktemp)"
gcloud storage cp "${cloud_bucket}/config.sh" "${config_file}" 2>/dev/null || return
save_variable "${config_file}" "${name}" "${value}"
gcloud storage mv "${config_file}" "${cloud_bucket}/config.sh"
}
function prepare_source_install {
# For whatever reasons different combinations of bazel environemnt seem to
# work differently wrt bazel-bin. This hack ensure that both synk and the
# files that synk will install are in bazel-bin
tmpdir="$(mktemp -d)"
bazel ${BAZEL_FLAGS} build //src/go/cmd/synk
cp -a ${DIR}/bazel-bin/src/go/cmd/synk/synk_/synk ${tmpdir}/synk
bazel ${BAZEL_FLAGS} build \
"@hashicorp_terraform//:terraform" \
"@kubernetes_helm//:helm" \
//src/app_charts/base:base-cloud \
//src/app_charts/platform-apps:platform-apps-cloud \
//src/app_charts:push \
//src/bootstrap/cloud:setup-robot.digest \
//src/go/cmd/setup-robot:setup-robot.push
mkdir -p ${DIR}/bazel-bin/src/go/cmd/synk/synk_/
mv -n ${tmpdir}/synk ${DIR}/bazel-bin/src/go/cmd/synk/synk_/synk
rm -f ${tmpdir}/synk
rmdir ${tmpdir} || /bin/true
# TODO(rodrigoq): the artifactregistry API would be enabled by Terraform, but
# that doesn't run until later, as it needs the digest of the setup-robot
# image. Consider splitting prepare_source_install into source_install_build
# and source_install_push and using Terraform to enable the API in between.
gcloud services enable artifactregistry.googleapis.com \
--project "${GCP_PROJECT_ID}"
# `setup-robot.push` is the first container push to avoid a GCR bug with parallel pushes on newly
# created projects (see b/123625511).
local oldPwd
oldPwd=$(pwd)
cd ${DIR}/bazel-bin/src/go/cmd/setup-robot/push_setup-robot.push.sh.runfiles/${RUNFILES_ROOT}
${DIR}/bazel-bin/src/go/cmd/setup-robot/push_setup-robot.push.sh \
--repository="${CLOUD_ROBOTICS_CONTAINER_REGISTRY}/setup-robot" \
--tag="latest"
# The tag variable must be called 'TAG', see cloud-robotics/bazel/container_push.bzl
# Running :push outside the build system shaves ~3 seconds off an incremental build.
cd ${DIR}/bazel-bin/src/app_charts/push.runfiles/${RUNFILES_ROOT}
TAG="latest" ${DIR}/bazel-bin/src/app_charts/push "${CLOUD_ROBOTICS_CONTAINER_REGISTRY}"
cd ${oldPwd}
}
function terraform_exec {
( cd "${TERRAFORM_DIR}" && ${TERRAFORM} "$@" )
}
function terraform_init {
local ROBOT_IMAGE_DIGEST
ROBOT_IMAGE_DIGEST=$(cat bazel-bin/src/bootstrap/cloud/setup-robot.digest)
# We only need to create dns resources if a custom domain is used.
local CUSTOM_DOMAIN
if [[ "${CLOUD_ROBOTICS_DOMAIN}" != "www.endpoints.${GCP_PROJECT_ID}.cloud.goog" ]]; then
CUSTOM_DOMAIN="${CLOUD_ROBOTICS_DOMAIN}"
fi
# This variable is set by src/bootstrap/cloud/run-install.sh for binary installs
local CRC_VERSION
if [[ -z "${TARGET}" ]]; then
# TODO(ensonic): keep this in sync with the nightly release script
VERSION=${VERSION:-"0.1.0"}
if [[ -d .git ]]; then
SHA=$(git rev-parse --short HEAD)
else
echo "WARNING: no git dir and no \$TARGET env set"
SHA="unknown"
fi
CRC_VERSION="crc-${VERSION}/crc-${VERSION}+${SHA}"
else
CRC_VERSION="${TARGET%.tar.gz}"
fi
cat > "${TERRAFORM_DIR}/terraform.tfvars" <<EOF
# autogenerated by deploy.sh, do not edit!
name = "${GCP_PROJECT_ID}"
id = "${GCP_PROJECT_ID}"
domain = "${CUSTOM_DOMAIN}"
zone = "${GCP_ZONE}"
region = "${GCP_REGION}"
shared_owner_group = "${CLOUD_ROBOTICS_SHARED_OWNER_GROUP}"
robot_image_reference = "${SOURCE_CONTAINER_REGISTRY}/setup-robot@${ROBOT_IMAGE_DIGEST}"
crc_version = "${CRC_VERSION}"
certificate_provider = "${CLOUD_ROBOTICS_CERTIFICATE_PROVIDER}"
cluster_type = "${GKE_CLUSTER_TYPE}"
datapath_provider = "${GKE_DATAPATH_PROVIDER}"
onprem_federation = ${ONPREM_FEDERATION}
secret_manager_plugin = ${GKE_SECRET_MANAGER_PLUGIN}
EOF
# Add certificate information if the configured provider requires it
if [[ ! "none self-signed" =~ (" "|^)"${CLOUD_ROBOTICS_CERTIFICATE_PROVIDER}"(" "|$) ]]; then
cat >> "${TERRAFORM_DIR}/terraform.tfvars" <<EOF
certificate_subject_common_name = "${CLOUD_ROBOTICS_CERTIFICATE_SUBJECT_COMMON_NAME}"
certificate_subject_organization = "${CLOUD_ROBOTICS_CERTIFICATE_SUBJECT_ORGANIZATION}"
EOF
if [[ -n "${CLOUD_ROBOTICS_CERTIFICATE_SUBJECT_ORGANIZATIONAL_UNIT}" ]]; then
cat >> "${TERRAFORM_DIR}/terraform.tfvars" <<EOF
certificate_subject_organizational_unit = "${CLOUD_ROBOTICS_CERTIFICATE_SUBJECT_ORGANIZATIONAL_UNIT}"
EOF
fi
fi
echo 'additional_regions = {' >> "${TERRAFORM_DIR}/terraform.tfvars"
local AR
for AR in "${ADDITIONAL_REGIONS[@]}"; do
local AR_NAME
local AR_REGION
local AR_ZONE
AR_NAME=$(jq -r .name <<<"${AR}")
AR_REGION=$(jq -r .region <<<"${AR}")
AR_ZONE=$(jq -r .zone <<<"${AR}")
cat >> "${TERRAFORM_DIR}/terraform.tfvars" <<EOF
${AR_NAME} = { region: "${AR_REGION}", zone: "${AR_ZONE}" },
EOF
done
echo '}' >> "${TERRAFORM_DIR}/terraform.tfvars"
# Docker private projects
if [[ -n "${PRIVATE_DOCKER_PROJECTS:-}" ]]; then
cat >> "${TERRAFORM_DIR}/terraform.tfvars" <<EOF
private_image_repositories = ["${PRIVATE_DOCKER_PROJECTS// /\", \"}"]
EOF
fi
# Terraform bucket
if [[ -n "${TERRAFORM_GCS_BUCKET:-}" ]]; then
cat > "${TERRAFORM_DIR}/backend.tf" <<EOF
# autogenerated by deploy.sh, do not edit!
terraform {
backend "gcs" {
bucket = "${TERRAFORM_GCS_BUCKET}"
prefix = "${TERRAFORM_GCS_PREFIX}"
}
}
EOF
else
rm -f "${TERRAFORM_DIR}/backend.tf"
fi
terraform_exec init -upgrade -reconfigure \
|| die "terraform init failed"
}
function terraform_apply {
terraform_init
terraform_exec apply ${TERRAFORM_APPLY_FLAGS} \
|| die "terraform apply failed"
terraform_post
}
function terraform_post {
local OLD_CLOUD_ROBOTICS_CTX
local location
OLD_CLOUD_ROBOTICS_CTX="${CLOUD_ROBOTICS_CTX}"
CLOUD_ROBOTICS_CTX=$(gke_context_name "${GCP_PROJECT_ID}" "cloud-robotics" "${GCP_REGION}" "${GCP_ZONE}")
[[ -z "${CLOUD_ROBOTICS_CTX}" ]] && die "no cloud-robotics cluster found"
if [[ "${OLD_CLOUD_ROBOTICS_CTX}" != "${CLOUD_ROBOTICS_CTX}" ]]; then
echo "updating CLOUD_ROBOTICS_CTX from ${OLD_CLOUD_ROBOTICS_CTX} to ${CLOUD_ROBOTICS_CTX}"
update_config_var ${GCP_PROJECT_ID} "CLOUD_ROBOTICS_CTX" "${CLOUD_ROBOTICS_CTX}"
fi
}
function terraform_delete {
terraform_init
terraform_exec destroy -auto-approve || die "terraform destroy failed"
}
function helm_region_shared {
local CLUSTER_CONTEXT
local CLUSTER_DOMAIN
local INGRESS_IP
local CLUSTER_REGION
local CLUSTER_ZONE
local CLUSTER_NAME
CLUSTER_CONTEXT="${1}"
CLUSTER_DOMAIN="${2}"
INGRESS_IP="${3}"
CLUSTER_REGION="${4}"
CLUSTER_ZONE="${5}"
CLUSTER_NAME="${6}"
gke_get_credentials "${GCP_PROJECT_ID}" "${CLUSTER_NAME}" "${CLUSTER_REGION}" "${CLUSTER_ZONE}"
# Wait for the GKE cluster to be reachable.
i=0
until kubectl --context "${CLUSTER_CONTEXT}" get serviceaccount default &>/dev/null; do
sleep 1
i=$((i + 1))
if ((i >= 60)) ; then
# Try again, without suppressing stderr this time.
if ! kubectl --context "${CLUSTER_CONTEXT}" get serviceaccount default >/dev/null; then
die "'kubectl get serviceaccount default' failed"
fi
fi
done
local BASE_NAMESPACE
BASE_NAMESPACE="default"
# Remove old unmanaged cert
if ! kubectl --context "${CLUSTER_CONTEXT}" get secrets cluster-authority -o yaml | grep -q "cert-manager.io/certificate-name: selfsigned-ca"; then
kubectl --context "${CLUSTER_CONTEXT}" delete secrets cluster-authority 2> /dev/null || true
fi
# Delete permissive binding if it exists from previous deployments
if kubectl --context "${CLUSTER_CONTEXT}" get clusterrolebinding permissive-binding &>/dev/null; then
kubectl --context "${CLUSTER_CONTEXT}" delete clusterrolebinding permissive-binding
fi
local values
values=(
--set-string "domain=${CLUSTER_DOMAIN}"
--set-string "ingress_ip=${INGRESS_IP}"
--set-string "project=${GCP_PROJECT_ID}"
--set-string "region=${CLUSTER_REGION}"
--set-string "registry=${SOURCE_CONTAINER_REGISTRY}"
--set-string "owner_email=${CLOUD_ROBOTICS_OWNER_EMAIL}"
--set-string "app_management=${APP_MANAGEMENT}"
--set-string "onprem_federation=${ONPREM_FEDERATION}"
--set-string "certificate_provider=${CLOUD_ROBOTICS_CERTIFICATE_PROVIDER}"
--set-string "deploy_environment=${CLOUD_ROBOTICS_DEPLOY_ENVIRONMENT}"
--set-string "oauth2_proxy.client_id=${CLOUD_ROBOTICS_OAUTH2_CLIENT_ID}"
--set-string "oauth2_proxy.client_secret=${CLOUD_ROBOTICS_OAUTH2_CLIENT_SECRET}"
--set-string "oauth2_proxy.cookie_secret=${CLOUD_ROBOTICS_COOKIE_SECRET}"
--set "use_tv_verbose=${CRC_USE_TV_VERBOSE}"
)
${SYNK_COMMAND} --context "${CLUSTER_CONTEXT}" init
echo "synk init done"
echo "installing base-cloud to ${CLUSTER_CONTEXT}..."
${HELM_COMMAND} --kube-context "${CLUSTER_CONTEXT}" template -n base-cloud --namespace=${BASE_NAMESPACE} "${values[@]}" \
./bazel-bin/src/app_charts/base/base-cloud-0.0.1.tgz \
| ${SYNK_COMMAND} --context "${CLUSTER_CONTEXT}" apply base-cloud -n ${BASE_NAMESPACE} -f - \
|| die "Synk failed for base-cloud"
# This is the main region. Only run this here!
if [[ "${CLUSTER_NAME}" = "${PROJECT_NAME}" ]]; then
echo "installing platform-apps-cloud to ${CLOUD_ROBOTICS_CTX}..."
${HELM_COMMAND} --kube-context "${CLUSTER_CONTEXT}" template -n platform-apps-cloud "${values[@]}" \
./bazel-bin/src/app_charts/platform-apps/platform-apps-cloud-0.0.1.tgz \
| ${SYNK_COMMAND} --context "${CLUSTER_CONTEXT}" apply platform-apps-cloud -f - \
|| die "Synk failed for platform-apps-cloud"
fi
}
function helm_main_region {
local INGRESS_IP
INGRESS_IP=$(terraform_exec output ingress-ip | tr -d '"')
helm_region_shared \
"${CLOUD_ROBOTICS_CTX}" \
"${CLOUD_ROBOTICS_DOMAIN}" \
"${INGRESS_IP}" \
"${GCP_REGION}" \
"${GCP_ZONE}" \
"${PROJECT_NAME}"
}
function helm_additional_region {
local ar_description
ar_description="${1}"
local AR_NAME
local AR_REGION
local AR_ZONE
AR_NAME=$(jq -r .name <<<"${ar_description}")
AR_REGION=$(jq -r .region <<<"${ar_description}")
AR_ZONE=$(jq -r .zone <<<"${ar_description}")
local CLUSTER_NAME
CLUSTER_NAME="${AR_NAME}-ar-cloud-robotics"
local INGRESS_IP
INGRESS_IP=$(terraform_exec output -json ingress-ip-ar | jq -r ."\"${CLUSTER_NAME}\"")
helm_region_shared \
$(gke_context_name "${GCP_PROJECT_ID}" "${CLUSTER_NAME}" "${AR_REGION}" "${AR_ZONE}") \
"${AR_NAME}.${CLOUD_ROBOTICS_DOMAIN}" \
"${INGRESS_IP}" \
"${AR_REGION}" \
"${AR_ZONE}" \
"${CLUSTER_NAME}"
}
function helm_charts {
helm_main_region
local AR
for AR in "${ADDITIONAL_REGIONS[@]}"; do
helm_additional_region "${AR}"
done
}
# commands
function set_config {
local project_id="$1"
${DIR}/scripts/set-config.sh "${project_id}"
}
function create {
include_config_and_defaults $1
if is_source_install; then
prepare_source_install
fi
terraform_apply
helm_charts
}
function delete {
include_config_and_defaults $1
if is_source_install; then
bazel ${BAZEL_FLAGS} build "@hashicorp_terraform//:terraform"
fi
terraform_delete
}
# Alias for create.
function update {
create $1
}
# This is a shortcut for skipping Terraform config checks if you know the config has not changed.
function fast_push {
include_config_and_defaults $1
if is_source_install; then
prepare_source_install
fi
helm_charts
}
# This is a shortcut for skipping building and applying Terraform configs if you know the build has not changed.
function update_infra {
include_config_and_defaults $1
terraform_apply
}
# main
if [[ "$#" -lt 2 ]] || [[ ! "$1" =~ ^(set_config|create|delete|update|fast_push|update_infra)$ ]]; then
die "Usage: $0 {set_config|create|delete|update|fast_push|update_infra} <project id>"
fi
# log and call arguments verbatim:
log $2 $0 $1
"$@"
================================================
FILE: docs/.gitignore
================================================
# Files created when running Jekyll locally, following
# https://help.github.com/en/articles/setting-up-your-github-pages-site-locally-with-jekyll
.sass-cache/
_site/
Gemfile
Gemfile.lock
================================================
FILE: docs/_config.yml
================================================
theme: jekyll-theme-slate
================================================
FILE: docs/concepts/app-management.md
================================================
# App Management
The Cloud Robotics Core application management (Layer 2) makes it easy to define and deploy
arbitrary applications across a fleet of cloud and robot clusters. The
[Helm v2 chart format](https://helm.sh/docs/developing_charts/) is used to define an application
at the scope of a single cluster. Additional custom resources tie them together to a
cross-cluster application and define their deployment. It relies on the Cloud Robotics Core
[federation layer](federation.md) to distribute the resources to the right clusters.
## App resource
The App resource defines a Cloud Robotics Core application by simply describing Helm charts for
two classes of clusters: cloud and robots. Charts may be specified inline as base64-encoded Helm
chart files or by referencing a Helm repository. App resources will be deployed to the cloud
cluster.
Example 1: application referencing charts from helm repositories
```yaml
apiVersion: apps.cloudrobotics.com/v1alpha1
kind: App
metadata:
name: ros-v1
spec:
repository: https://my.repo
version: 1.2.1
components:
cloud:
name: ros-cloud
robot:
name: ros-robot
```
Example 2: application using inline as base64-encoded charts (development workflow)
```yaml
apiVersion: apps.cloudrobotics.com/v1alpha1
kind: App
metadata:
name: ros-v1
spec:
components:
cloud:
chart:
inline: <base64>
robot:
chart:
inline: <base64>
```
Right now we only have bazel build rules to produce inline charts.
## AppRollout Resource
An AppRollout describes how a defined App should be deployed across a fleet of clusters. It allows
to flexibly select robots which should run an application and to inject fine-grained configuration.
Example 3: AppRollout with different configuration options per target
```yaml
apiVersion: apps.cloudrobotics.com/v1alpha1
kind: AppRollout
metadata:
name: ros-stable
labels:
app.kubernetes.io/name: ros
role: navtest
release: stable
spec:
appName: ros-v1
cloud:
values:
override: foo
robots:
- selector:
matchLabels:
model: mir100
values:
override1: bar
- selector:
matchLabels:
model: mir200
values:
override1: baz
version: v1.2.2 # Chart version override for canarying
```
AppRollouts are deployed into the cloud cluster, where a controller (app-rollout-controller) handles them.
The controller applies the specified selectors and creates or updates the internal
ChartAssignments. A ChartAssignment represents a single instance of a chart that should be
installed into a single cluster. These internal objects describe the task of installing parts of
the application.
The federation layer will sync ChartAssignments to robots as needed. The actual
installation is done by another controller (chart-assignment-controller), this
time running both in the cloud and on the robots. The AppRollout controller will
watch the status updates and consolidate the information into status updates on
the AppRollout.
## Sharing secrets
If you create a Secret in the `default` namespace labelled
`cloudrobotics.com/copy-to-chart-namespaces=true`, it will be copied into all
namespaces created by the chart-assignment-controller. This is useful for
cluster-specific license keys that can be used by applications.
## Opt a pod out of status checking
During rollout, the chart-assignment-controller checks for Pods in the rollout being `Running` or `Completed`.
In some cases, this check is not necessary or might need to be opted out of.
In this case, add a label `cloudrobotics.com/opt-out-error-checking=true` to your pods. Adding this
instructs the chart-assignment-controller to not block the status from reaching `Ready`.
================================================
FILE: docs/concepts/config.md
================================================
# Project configuration
The project configuration that one has entered during the initial setup is
stored with the project in GCS. One can look at the options with the following
command:
```shell
gcloud storage cat gs://${PROJECT_ID}-cloud-robotics-config/config.sh
```
The settings contained in the config file are used by terraform to setup the
project infrastructure and used by the cloud and chart-assignment-controller services running
in kubernetes to configure apps.
The terraform support is encapsulated in deploy.sh that creates a temporary
`terraform.tfvars` file.
To support configuring apps, we pass the settings to app-rollout-controller where they are
provided as additional variables for helm templating. The command below prints
the settings we pass to app-rollout-controller:
```shell
kubectl get deployment app-rollout-controller -o=jsonpath='{.spec.template.spec.containers[0].args[0]}'
```
================================================
FILE: docs/concepts/device_identity.md
================================================
# Device Identity
Device Identity, part of Layer 1, provides an identity for robot clusters and
services to integrate those identities into a cloud based IAM system.
The following components are part of the whole setup:
* Cloud:
* `IAM`: Cloud Identity and Access Management
* `Kubernetes configmaps`: used as a Key Management Service
* `Token Vendor`: token exchange service for OAuth2 service accounts
* `robot service-account`: a GCP IAM service account that has the union of
permissions that applications running on the robot cluster require
* Robot cluster (on-prem or edge):
* `Metadata Server`: provides default credentials + project metadata
* `Setup`: special app used to register the workcell
* `<App>`: any app accessing the cloud
The following chapters explain the flows in more detail. Further information
about the Token Vendor can be found in its
[docs](https://github.com/googlecloudrobotics/core/tree/master/src/go/cmd/token-vendor/README.md)
## Setup
The setup flow is used to register a new robot cluster to a cloud project.

* (1) (Admin-)user runs `Setup`, which generates a RSA key-pair and stores it as
a K8S secret
* (2) `Setup` uploads the public key to `Token Vendor`
* (3) `Token Vendor` stores key in `Kubernetes`
## Authentication
The authentication flow is used to transparently make cloud API calls work for
on-prem robot clusters.

* `<App>` creates an API client without loading any custom key material
* (1) API client library probes `Metadata Server` to get ADCs (Application
Default Credentials)
* (2) `Metadata Server` talks to `Token Vendor` get an Access Token for the
`robot service-account`
* (3) `Token Vendor` verifies the key the request has been signed with against
the device registry
* (4) `Token Vendor` gets an Access Token for the `robot service-account` from
`IAM`
* `Token Vendor` returns Access Token through `Metadata Server` to the
`<App>` and that can use it to call Cloud APIs under the scope of the
`robot service-account`
================================================
FILE: docs/concepts/federation.md
================================================
# Federation
Federation, part of Layer 1, is responsible for synchronizing the state between robot and cloud
clusters. Configuration state in Cloud Robotics Core is primarily expressed through [custom
resources](https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/)
by platform and user applications alike. Our federation system enables other components to use
custom resources locally without needing to be aware of the multi cluster setup and the quality
of the network connection.
## Semantics
A Kubernetes resource is typically divided into a [“spec” and a “status”](https://kubernetes.io/docs/concepts/overview/working-with-objects/kubernetes-objects/#object-spec-and-status)
section. The `spec` section expresses the intent of the resource, typically authored by a user
or another application along with all its metadata.
The `status` section must generally only be written to by the controller that is responsible for
realizing the specification. Consequently, `spec` and `status` typically each have one distinct
author.
For federating resources, this means that `spec` and `status` of a resource are owned by at most
one cluster respectively (possibly the same one). The cluster owning the spec is also the main
owner of the resource overall and controls its lifecycle, i.e. deletion.
A resource’s spec is always synced from an upstream cluster to a downstream
cluster and its status synced back from downstream to upstream.
All resources of a specific type may either be synchronized to all robots or to exactly one robot.
There is no direct synchronization between robots. However, a robot may create a resource in the
cloud cluster that will be distributed to other robots.
If a resource owned by the upstream cluster has been synchronized to one or more
downstream clusters, it can only be permanently deleted upstream: if deleted
downstream, it will be recreated. If deleted in the upstream cluster, it will be
asynchronously deleted in other clusters that hold a copy of the resource.
Upstream deletion can complete before the downstream resource is deleted.
## cr-syncer
The cr-syncer component (Custom Resource Syncer) is a controller that runs inside each robot
cluster. It is connected to the Kubernetes API servers of the cloud and the robot cluster alike
and continuously watches for updates on custom resources. The controller contains retry and resync
logic to address intermittent connectivity.

The behavior of the cr-syncer can be configured per custom resource definition (CRD) by setting
annotations on its CRD:
* `cr-syncer.cloudrobotics.com/spec-source`: may be `cloud` or `robot`. It determines which
cluster type owns metadata, spec, and lifecycle of all resources of the CRD. It implies
that the other cluster type owns the status section.
* `cr-syncer.cloudrobotics.com/filter-by-robot-name`: a boolean that determines whether resources
will be synced to all robots or just a single one. An individual resource is labeled with
`cloudrobotics.com/robot-name` to indicate which robot it should be synced to. If the label
is missing on a resource, it will not be synced at all.
* `cr-syncer.cloudrobotics.com/status-subtree`: a string key, which defines which sub-section of
the resource status is synced from the downstream cluster. This lets you split
a resource’s status into `robot` and `cloud` sections, for example. Using this
annotation is generally discouraged as it likely points to a flaw in the
modeling of the respective CRD.
## Deletion
When the cr-syncer sees a resource in the downstream cluster with no
corresponding resource in upstream cluster, it deletes it. This handles orphaned
resources when the upstream resource was deleted while the cr-syncer was
restarting. It also means that you can't create a resource directly in the
downstream cluster. The upstream resource is identified using the namespace and
name, but not the UID, so deletion and recreation upstream may result in an
update in the downstream cluster.
> **Note**: if you create a resource directly in the downstream cluster, the
> behavior will depend on how the CRD is annotated. If `filter-by-robot-name`
> is false, the cr-syncer will delete all downstream resources that don't
> correspond to upstream resources. This means that by listing CRs in the
> upstream cluster, you can reason about which CRs will exist in the downstream
> cluster.
>
> If `cr-syncer.cloudrobotics.com/filter-by-robot-name` is true, then the
> cr-syncer will ignore any downstream resources that are not labelled with a
> matching robot name. This means that a robot can run ChartAssignments that are
> synced from the cloud as well as those created directly in the robot cluster.
In some cases, downstream deletion may be blocked. For example, if we have
deleted an upstream ChartAssignment, but the chart-assignment-controller has failed to remove
its finalizer from the downstream ChartAssignment. This edge case leads to
surprising behavior:
- An upstream ChartAssignment can be recreated before the downstream
ChartAssignment is deleted.
- The old status from the downstream cluster will be synced to the new upstream
ChartAssignment.
If needed, this can be detected by watching the downstream cluster after
deleting the resource from the downstream cluster. The situation will clean up
once downstream deletion is complete.
Note: previously, the cr-syncer used finalizers to block upstream deletion
until the downstream resource was deleted. This gave the original deleter more
information: for example, once an AppRollout had been deleted in the cloud, it
means that all robots have terminated the app's pods. However, this caused
problems with offline or renamed clusters: an admin would have to manually clean
up the old finalizers. The new asynchronous behavior is not affected by offline
clusters.
## Resource generations
Custom resources have a field `.metadata.generation` that starts at 1 and is
incremented when the resource changes. Specifically, if the CRD enables the
/status subresource, the generation increases by 1 every time the resource spec
changes, but not when the status changes. The resource controller can set
`.status.observedGeneration` to the latest generation it has observed, so the
user can change the spec, then wait for `observedGeneration` to catch up before
looking at the status. For example:
* Create a Deployment for one pod (generation=1), and wait for the status to be Ready.
* Change the Deployment's image reference (generation=2): the status is still
Ready, but this refers the old spec (observedGeneration=1).
* Wait for the status to update (observedGeneration=2): now the status is
non-ready, referring to the newer spec.
* Wait for the status to be Ready. The new image is now running.
`generation` and `observedGeneration` can **only be compared in the downstream
cluster**. As the generation is managed by the Kubernetes apiserver, the
cr-syncer cannot guarantee that the upstream generation matches the downstream
generation. On the other hand, `observedGeneration` will be copied from
downstream to upstream with the rest of `.status`. This means that `generation`
is cluster-specific but `observedGeneration` always refers to the downstream
generation.
================================================
FILE: docs/developers/debug-auth.md
================================================
# Debugging authentication problems
Useful tips for working with Authentication and Authorization systems.
## Run a sample request with various credentials
You can call Cloud APIs with curl to see whether authorization works.
### Your own credentials
```bash
PROJECT_NUMBER=201199916163
curl -v -H "Content-Type: application/json" \
-H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \
"https://cloudroboticssensordata.googleapis.com/v1eap/projects/${PROJECT_NUMBER}/sensors"
```
### Service account JSON file
You can create a JSON file with the robot account's credentials on
the [Cloud console's credentials page](https://console.cloud.google.com/apis/credentials).
```bash
PROJECT_NUMBER=201199916163
JSON_CREDENTIALS=/tmp/my-project-b7364a68fa92.json
curl -v -H "Content-Type: application/json" \
-H "Authorization: Bearer $(GOOGLE_APPLICATION_CREDENTIALS=${JSON_CREDENTIALS} gcloud auth application-default print-access-token)" \
"https://cloudroboticssensordata.googleapis.com/v1eap/projects/${PROJECT_NUMBER}/sensors"
```
### Get an OAuth token from IAM
The token vendor doesn't have its own keys, but instead calls IAM's
generateAccessToken method. You can emulate its behavior by using the [API
Explorer](https://developers.google.com/apis-explorer/#search/iam%20credentials/iamcredentials/v1/iamcredentials.projects.serviceAccounts.generateAccessToken)
to call `iamcredentials.projects.serviceAccounts.generateAccessToken`. The name
parameter is
`projects/-/serviceAccounts/robot-service@my-project.iam.gserviceaccount.com`,
and the `scope` is `https://www.googleapis.com/auth/cloud-platform`.
You can pass the returned access token in an Authorization header as above.
## Check whether the client's request is well-formed and authenticated
The easiest way to verify that metadata server and the gRPC client library
are doing the right thing is to use a logging HTTP server as the gRPC server.
Instead of setting the gRPC host to the Cloud API server
(`cloudroboticssensordata.googleapis.com`), you set it to an
HTTPS-capable server under your control. You need HTTPS support because
otherwise the gRPC library will rightfully decline to send access tokens.
Luckily, your Cloud Robotics Core setup already runs an HTTPS server. Suppose
you're calling the gRPC service
`google.cloud.robotics.sensordata.v1eap.SensorDataService`. You can
hook a very simple Python HTTP server into your cloud nginx setup like
this:
```yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: debug
spec:
server.py: |
import BaseHTTPServer
import SocketServer
class MyHandler(BaseHTTPServer.BaseHTTPRequestHandler):
def do_GET(self):
print "Got request for ", self.path, " with auth ", self.headers.get('Authorization')
def do_POST(self):
print "Got request for ", self.path, " with auth ", self.headers.get('Authorization')
httpd = SocketServer.TCPServer(("", 8080), MyHandler)
httpd.serve_forever()
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: debug
spec:
selector:
matchLabels:
app: debug
replicas: 1
template:
metadata:
labels:
app: debug
spec:
containers:
- name: python
image: python:2
args: ["python", "/src/server.py"]
volumeMounts:
- name: src-volume
mountPath: /src
volumes:
- name: src-volume
configMap:
name: debug
---
apiVersion: v1
kind: Service
metadata:
labels:
app: debug
name: debug
spec:
ports:
- name: http
port: 8082
protocol: TCP
targetPort: 8080
selector:
app: debug
type: ClusterIP
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
nginx.ingress.kubernetes.io/backend-protocol: HTTP
name: debug
spec:
ingressClassName: nginx
rules:
- host: www.endpoints.my-project.cloud.goog
http:
paths:
- path: /google.cloud.robotics.sensordata.v1eap.SensorDataService
pathType: Prefix
backend:
service:
name: debug
port
number: 8080
```
This will log the Authorization header to the pod's stdout, so you can view it
with `kubectl logs`. Save the token to a file (don't paste it into the command
line because it will end up in your shell history).
## Checking tokens with the tokeninfo service
You can check the token's contents and sanity with Google's tokeninfo endpoint:
```shell
curl https://oauth2.googleapis.com/tokeninfo?access_token=$(cat /tmp/token.txt)
```
================================================
FILE: docs/how-to/connecting-robot.md
================================================
# Connecting a robot to the cloud
Estimated time: 10 min
This page describes how to connect a Kubernetes cluster on a robot running Ubuntu 20.04 to the cloud.
Once you've done this, you can:
* Run a private Docker container from the Google Container Registry
* Securely communicate with cloud services
* See logs from the robot in the Cloud Console
## Setting up the GCP project
1. If you haven't already, complete the [Setting up the GCP project](../quickstart.md) steps.
1. On the computer you used to set up the cloud project, generate an access token, which you'll use to give the robot access to the cloud:
```shell
gcloud auth application-default print-access-token
```
> **Note:** If you want to reduce the risk that your cloud project is
> compromised using this token during its 1h lifetime, you can generate a less
> privileged service account token:
>
> ```
> SA="human-acl@${PROJECT_ID}.iam.gserviceaccount.com"
> gcloud iam service-accounts add-iam-policy-binding "${SA}" \
> --role=roles/iam.serviceAccountTokenCreator \
> --project="${PROJECT_ID}" --member="user:${YOUR_EMAIL_ADDRESS:?}"
> gcloud auth print-access-token --impersonate-service-account="${SA}"
> ```
>
> If you see `ERROR: Failed to impersonate ...`, wait a few minutes for the IAM
> policy to propagate.
>
> You can ignore the "WARNING: This command is using service account
> impersonation."
## Installing the cluster on the robot
## Installing Kubernetes
You'll need to install a Kubernetes cluster on the robot before you can connect it to the cloud. The cluster manages and supports the processes that communicate with the cloud.
Please see external references for setting up k8s. For simplicity we recommend
[k3s](https://k3s.io/) or a single node
[kubeadm](https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/create-cluster-kubeadm/)
cluster (untested).
## Setting up the robot
1. Set up the robot cluster to connect to the cloud. When running `setup_robot.sh`, you'll need to enter the access token you generated earlier. You may find it easiest if you SSH into the robot from the workstation you used to set up the project.
```shell
mkdir -p ~/cloud-robotics-core
cd ~/cloud-robotics-core
curl https://raw.githubusercontent.com/googlecloudrobotics/core/master/src/bootstrap/robot/setup_robot.sh >setup_robot.sh
bash setup_robot.sh my-robot --project ${PROJECT_ID} \
--robot-type my-robot-type
```
Set `${PROJECT_ID}` to your GCP project ID. When prompted for an access token, provide the authentication token you generated earlier.
> **Note:** `my-robot-type` is a placeholder and you can ignore it for now.
## What's next
* [Using Cloud Storage from a robot](using-cloud-storage.md).
================================================
FILE: docs/how-to/creating-declarative-api.md
================================================
# Creating a declarative API
<!-- Estimated time: TODO -->
In this guide we will use a Kubernetes-style declarative API to interface to an external Charge Service for a robot.
This API is built around the concept of a ChargeAction resource, which instructs a robot to drive to a charger.
While the robot is charging, the status of the ChargeAction resource is kept up-to-date and can be observed.
## Motivation
RPC-based systems like ROS's [actionlib](http://wiki.ros.org/actionlib), while proven to be scalable, maintainable and useful, leave a few things to be desired:
1. **Synchronization**. The intent for a controller is stored in-memory in multiple components and we rely on correct synchronization.
For example, the motion planner sends the "turn wheel 3 times per second" message to the wheel actuator, then trusts that the wheel actuator will have received the intent and waits for it to act on the shared intent.
If a second process (such as an emergency stop) overwrites the intent of the wheel actuator, there's no standard channel to notify the motion planner.
2. **Persistence**. Since the intent is stored in-memory, it is lost when any process restarts.
This is the core reason that software in ROS systems can't be updated on the fly.
3. **Inspection**. For debugging, a coherent view into the current system intent would be great.
In RPC-based APIs, the intent is often updated differentially (eg "a little more to the left"), so our only hope of debugging is to log all messages ever sent.
In a declarative API, all actions and feedback are stored in a shared database—an approach built on Kubernetes' experience building robust distributed systems—which addresses these issues.
The latency added by going through the shared database means that
declarative APIs are best suited to latency-tolerant applications like
high-level control.
<!-- ## Concepts (describe resources, controllers, etc, and link to docs) -->
## Prerequisites
* Completed the [Quickstart Guide](../quickstart.md), after which the GCP project is set up and `gcloud-sdk` and `kubectl` are installed and configured.
* `docker` is installed and configured on the workstation ([instructions](https://docs.docker.com/install/linux/docker-ce/ubuntu/)).
<!-- * For the last part of the guide: A robot that has been successfully [connected to the cloud](connecting-robot.md). -->
Create a directory for the code examples of this guide, e.g.:
```shell
mkdir charge-service
cd charge-service
```
Set your GCP project ID as an environment variable:
```
export PROJECT_ID=[YOUR_GCP_PROJECT_ID]
```
All files created in this tutorial can be found in
[docs/how-to/examples/charge-service](https://github.com/googlecloudrobotics/core/tree/master/docs/how-to/examples/charge-service).
If you download the files, you have to replace the placeholder `[PROJECT_ID]` with your GCP project ID:
```shell
sed -i "s/\[PROJECT_ID\]/$PROJECT_ID/g" charge-controller.yaml
```
## Installing metacontroller
This tutorial is based on [metacontroller](https://metacontroller.github.io/metacontroller/intro.html), an add-on for Kubernetes that makes it easy to write and deploy [custom controllers](https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/#custom-controllers).
Custom controllers implement the logic behind a declarative API.
First, make sure that `kubectl` points to the correct GKE cluster:
```shell
kubectl config get-contexts
```
If the correct cluster is not marked with an asterisk in the output, you can switch to it with `kubectl config use-context [...]`.
Now install metacontroller to the cloud-cluster:
```shell
kubectl create namespace metacontroller
kubectl apply -f https://raw.githubusercontent.com/metacontroller/metacontroller/master/manifests/production/metacontroller-rbac.yaml
kubectl apply -f https://raw.githubusercontent.com/metacontroller/metacontroller/master/manifests/production/metacontroller-crds-v1.yaml
kubectl apply -f https://raw.githubusercontent.com/metacontroller/metacontroller/master/manifests/production/metacontroller.yaml
```
Let's check that all resources came up:
```console
> kubectl get pods --namespace metacontroller
NAME READY STATUS RESTARTS AGE
metacontroller-0 1/1 Running 0 1m
```
You can learn more details in the metacontroller's [install instructions](https://metacontroller.github.io/metacontroller/guide/install.html).
> **Limitations of metacontroller**:
> Writing custom controllers with metacontroller is easy, and you can use
> whatever programming language you prefer.
> However, it has some limitations.
>
> 1. metacontroller can't directly detect changes in external state, although
> you can configure it to periodically reconcile your resources with external
> systems. This introduces latency corresponding to the reconciliation
> frequency.
> 1. The information available to your controller is limited.
> You can't use metacontroller to create a controller that only acts on a
> single resource out of many (for example, a controller that only executes
> the highest-priority action).
>
> For advanced use cases, writing a controller in Golang offers more
> flexibility. See
> [sample-controller](https://github.com/kubernetes/sample-controller) for an
> example.
## Defining the controller logic
The core of a declarative API is the controller logic, which defines how the resources should be handled and reports the current status.
For the Charge Service, we've implemented the logic in a Python script.
Download [server.py](examples/charge-service/server.py):
```shell
curl -O https://raw.githubusercontent.com/googlecloudrobotics/core/master/docs/how-to/examples/charge-service/server.py
```
This Python program implements a server that listens on port 80 for incoming HTTP POST requests from metacontroller.
The controller logic is contained in the `sync()` method, which handles new ChargeActions by calling `charge_service.start_charging()`, and handles in-progress ChargeActions by updating the status.
[embedmd]:# (examples/charge-service/server.py python /.*state = current_status.get/ /return.*status.*/)
```python
state = current_status.get("state", "CREATED")
if state == "CREATED":
# The ChargeAction has just been created. Use the external Charge Service
# to start charging. Store the request ID in the status so we can use it
# to check the state of the charge request.
request_id = self.charge_service.start_charging()
desired_status["state"] = "IN_PROGRESS"
desired_status["request_id"] = request_id
elif state == "IN_PROGRESS":
try:
# Get the progress of the charge request from the external service.
progress = self.charge_service.get_progress(
current_status["request_id"])
desired_status["charge_level_percent"] = progress
if progress == 100:
# Charging has completed.
desired_status["state"] = "OK"
except ValueError as e:
# The charge request was not found. This could be because the robot was
# restarted during a charge, and the request was forgotten.
desired_status["state"] = "ERROR"
desired_status["message"] = str(e)
elif state in ["OK", "CANCELLED", "ERROR"]:
# Terminal state, do nothing.
pass
else:
desired_status["state"] = "ERROR"
desired_status["message"] = "Unrecognized state: %r" % state
return {"status": desired_status, "children": []}
```
## Dockerizing the service
Next, to prepare our controller logic for deployment in the cloud, we package it as a Docker image. Make sure that the docker daemon is running and that your user has the necessary privileges:
```shell
docker run --rm hello-world
```
If this command fails, make sure Docker is installed according to the [installation instructions](https://docs.docker.com/install/linux/docker-ce/ubuntu/).
In the same directory as `server.py`, create a `Dockerfile` with the following contents:
[embedmd]:# (examples/charge-service/Dockerfile dockerfile)
```dockerfile
FROM python:alpine
WORKDIR /data
COPY server.py ./
CMD [ "python", "-u", "./server.py" ]
```
(Note: the `-u` option disables line-buffering; Python's line-buffering can prevent output from appearing immediately in the Docker logs.)
To build the Docker image, run:
```shell
docker build -t charge-controller .
```
You should see `Successfully tagged charge-controller:latest`. You can run the container locally with:
```shell
docker run -ti --rm -p 8000:8000 charge-controller
```
Then, from another terminal on the same workstation, send a request with an empty `parent` object:
```shell
curl -X POST -d '{"parent": {}, "children": []}' http://localhost:8000/
```
You should see a response like:
```json
{"status": {"state": "IN_PROGRESS", "request_id": "2423e70c-9dc7-47ac-abcb-b2ef0cbc676c"}, "children": []}
```
The response indicates that the controller would set `"state": "IN_PROGRESS"` on a newly-created ChargeAction.
## Uploading the Docker image to the cloud
In order to be able to run the server as a container in our cloud cluster, we need to upload the Docker image to our GCP project's private [container registry](https://cloud.google.com/container-registry/docs/pushing-and-pulling).
Enable the Docker credential helper:
```shell
gcloud auth configure-docker
```
Tag the image and push it to the registry:
```shell
docker tag charge-controller gcr.io/$PROJECT_ID/charge-controller
docker push gcr.io/$PROJECT_ID/charge-controller
```
The image should now show up in the [Container Registry](https://console.cloud.google.com/gcr).
## Deploying the declarative API in the cloud
Create a file called `charge-crd.yaml` with the following contents:
[embedmd]:# (examples/charge-service/charge-crd.yaml yaml)
```yaml
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: chargeactions.example.com
annotations:
cr-syncer.cloudrobotics.com/spec-source: cloud
spec:
group: example.com
names:
kind: ChargeAction
plural: chargeactions
singular: chargeaction
scope: Namespaced
versions:
- name: v1
served: true
storage: true
subresources:
status: {}
schema:
openAPIV3Schema:
type: object
x-kubernetes-preserve-unknown-fields: true
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: cloud-robotics:cr-syncer:chartaction
labels:
cr-syncer.cloudrobotics.com/aggregate-to-robot-service: "true"
rules:
- apiGroups:
- example.com
resources:
- chargeactions
verbs:
- get
- list
- watch
- update
```
This is a [custom resource](https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/) definition (CRD) for a resource called ChargeAction.
This simple example just describes the name and version of the API, but CRDs can also define schemas for the resources.
The ClusterRole configures [role-based access control](https://kubernetes.io/docs/reference/access-authn-authz/rbac/) to let the robot access the ChargeActions.
Don't worry about the `cr-syncer.cloudrobotics.com/spec-source` annotation for now, as it'll be explained later in the tutorial.
Next, create a file called `charge-controller.yaml` with the following contents, replacing `[PROJECT_ID]` with your GCP project ID:
[embedmd]:# (examples/charge-service/charge-controller.yaml yaml)
```yaml
apiVersion: metacontroller.k8s.io/v1alpha1
kind: CompositeController
metadata:
name: charge-controller
spec:
generateSelector: true
parentResource:
apiVersion: example.com/v1
resource: chargeactions
resyncPeriodSeconds: 1
hooks:
sync:
webhook:
url: http://charge-controller.default:8000/sync
---
apiVersion: v1
kind: Service
metadata:
name: charge-controller
spec:
selector:
app: charge-controller
ports:
- port: 8000
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: charge-controller
spec:
replicas: 1
selector:
matchLabels:
app: charge-controller
template:
metadata:
labels:
app: charge-controller
spec:
containers:
- name: controller
image: gcr.io/[PROJECT_ID]/charge-controller
ports:
- containerPort: 8000
```
This file contains the information needed by Kubernetes and metacontroller to handle ChargeAction resources.
In the following, we will go over it bit by bit assuming basic familiarity with the [YAML format](https://en.wikipedia.org/wiki/YAML).
We define three Kubernetes resources:
* The *CompositeController* tells metacontroller to send ChargeAction resources to the charge-controller Service.
* The *Service* defines how the HTTP server is exposed within the cluster.
* The *Deployment* describes the Docker container to run.
Metadata, labels, and selectors are used to tie the three resources together.
A detailed explanation of the Kubernetes resources is out of scope for this guide, check out the [Kubernetes docs](https://kubernetes.io/docs/home/) or [metacontroller User Guide](https://metacontroller.github.io/metacontroller/guide.html) to get started.
There are a few points worth mentioning, though:
* In the Deployment, don't forget to replace `[PROJECT_ID]` with your GCP project ID.
* The CompositeController sets `resyncPeriodSeconds: 1`.
This tells metacontroller to check each ChargeAction every second.
This allows `server.py` to update the progress every second while the action is in progress.
* The CompositeController sets `url: http://charge-controller.default:8000/sync`.
This tells metacontroller that the ChargeAction resources are handled by a service called `charge-controller` in the `default` namespace.
Deploy these resources by applying the configuration:
```shell
kubectl apply -f charge-crd.yaml
kubectl apply -f charge-controller.yaml
```
You can explore the various resources that were created on your cluster as a result of this command in the [GKE Console](https://console.cloud.google.com/kubernetes/workload) or with `kubectl`, e.g.:
```shell
kubectl get pods
```
The resulting list should contain a running pod with a name like `charge-controller-xxxxxxxxxx-xxxxx`.
## Redeploying after a change
If you make a change to `server.py`, you need to rebuild and push the Docker image:
```shell
docker build -t charge-controller .
docker tag charge-controller gcr.io/$PROJECT_ID/charge-controller
docker push gcr.io/$PROJECT_ID/charge-controller
```
The easiest way to get Kubernetes to restart the workload with the latest version of the container is to delete the pod:
```shell
kubectl delete pod -l 'app=charge-controller'
```
Kubernetes will automatically pull the newest image and recreate the pod.
If you make a change to `charge-controller.yaml`, all you have to do is apply it again:
```shell
kubectl apply -f charge-controller.yaml
```
## Accessing the API
You can use `kubectl` to interact with the API.
Create a file called `charge-action.yaml` with the following contents:
[embedmd]:# (examples/charge-service/charge-action.yaml yaml)
```yaml
apiVersion: example.com/v1
kind: ChargeAction
metadata:
name: my-charge-action
```
Run the following command to create a ChargeAction and observe how its status changes:
```shell
kubectl apply -f charge-action.yaml \
&& watch -n0 kubectl describe chargeaction my-charge-action
```
Over the next 10 seconds, you should see the "Charge Level Percent" increase to 100, and then the state should become "CHARGED".
> **Troubleshooting**:
> If the ChargeAction's status doesn't change, check that metacontroller is installed by running `kubectl --namespace metacontroller get pods`.
> You should see `metacontroller-0 1/1 Running`.
> You can also check the metacontroller logs with `kubectl --namespace metacontroller logs metacontroller-0`
## Deploying the declarative API on the robot.
So far, the Charge Service has been running in the cloud, but we need to run
code on the robot to get it to charge.
We can change this with the `cr-syncer`, a component of Cloud Robotics Core that allows declarative APIs to work between Kubernetes clusters.
In particular, we can run the charge-controller on the robot, while creating the ChargeAction in the cloud cluster.
The `cr-syncer` takes care of copying the ChargeAction to the robot when the
robot has network connectivity.
**Prerequisite**: you'll need a robot that has been successfully [connected to the cloud](connecting-robot.md).
First, remove the controller from the cloud cluster:
```shell
# Note: run this on the workstation
kubectl delete -f charge-controller.yaml
```
Then SSH into the robot, install metacontroller, and bring up the charge-controller there:
```shell
# Note: run this on the robot
kubectl create namespace metacontroller
kubectl apply -f https://raw.githubusercontent.com/metacontroller/metacontroller/master/manifests/production/metacontroller-rbac.yaml
kubectl apply -f https://raw.githubusercontent.com/metacontroller/metacontroller/master/manifests/production/metacontroller-crds-v1.yaml
kubectl apply -f https://raw.githubusercontent.com/metacontroller/metacontroller/master/manifests/production/metacontroller.yaml
export PROJECT_ID=[YOUR_GCP_PROJECT_ID]
kubectl apply -f https://raw.githubusercontent.com/googlecloudrobotics/core/master/docs/how-to/examples/charge-service/charge-crd.yaml
curl https://raw.githubusercontent.com/googlecloudrobotics/core/master/docs/how-to/examples/charge-service/charge-controller.yaml \
| sed "s/\[PROJECT_ID\]/$PROJECT_ID/g" | kubectl apply -f -
```
Now, check that these are running correctly:
```console
# Note: run this on the robot
> kubectl get pods --namespace metacontroller
NAME READY STATUS RESTARTS AGE
metacontroller-0 1/1 Running 0 1m
> kubectl get pods -l app=charge-controller
NAME READY STATUS RESTARTS AGE
charge-controller-57786849f8-xp5kf 1/1 Running 0 77s
```
Switch back to a terminal on your workstation.
As before, you can create a ChargeAction with `kubectl`, but this time it will be
handled by the controller on the robot.
```shell
# Note: run this on the workstation
kubectl delete -f charge-action.yaml
kubectl apply -f charge-action.yaml \
&& watch -n0 kubectl describe chargeaction my-charge-action
```
How does this work?
- The `cr-syncer` runs on the robot and watches custom resources in the cloud.
- It sees the `cr-syncer.cloudrobotics.com/spec-source: cloud` annotation on the
CustomResourceDefinition, which tells it to copy the `spec` from
`my-charge-action` in the cloud cluster into a copy of `my-charge-action` in
the robot cluster.
- While the robot is charging, the robot's charge-controller updates the status
in the robot's cluster.
- The `cr-syncer` copies the status back up to the original resource in the
cloud cluster.
## Cleaning up
In order to stop the controller and remove the CRD you created, run:
```shell
kubectl delete -f charge-controller.yaml -f charge-crd.yaml
```
If you want to uninstall metacontroller too, run:
```shell
kubectl delete namespace metacontroller
```
If you installed on the robot, you'll need to run these commands there too.
<!--
TODO(rodrigoq): define "What's Next" for declarative APIs
## What's next
There are a few inconvenient steps in this guide, e.g.:
* manually replacing the project ID in all source files or
* remotely logging in to the robot to start a workload.
This is where the app management layer comes in; it provides, among other capabilities:
* ways of bundling and parameterizing Kubernetes resources and
* remote management of Kubernetes resources/workloads on the robot.
Also note that both the server and the client side can be implemented with similar ease in other programming languages, such as [Go](https://github.com/googleapis/google-api-go-client).
-->
================================================
FILE: docs/how-to/deploy-from-sources.md
================================================
# Deploy Cloud Robotics Core from sources
Estimated time: 30 min
This page describes how to set up a Google Cloud Platform (GCP) project
containing the Cloud Robotics Core components.
In particular, this creates a cluster with Google Kubernetes Engine and prepares
it to accept connections from robots, which enables those robots to securely
communicate with GCP.
The commands were tested on machines running Debian (Stretch) or Ubuntu (16.04
and 18.04) Linux.
1. In the GCP Console, go to the [Manage resources][resource-manager] page and
select or create a project.
1. Make sure that [billing][modify-project] is enabled for your project.
1. [Install the Cloud SDK][cloud-sdk]. When prompted, choose the project that you created above.
1. After installing the Cloud SDK, install the `kubectl` command-line tool and the GKE auth plugin:
```shell
gcloud components install kubectl
gcloud components gke-gcloud-auth-plugin
```
If you're using Debian or Ubuntu, you may need to use `apt install kubectl` instead.
1. [Install the Bazel build system][install-bazel].
1. Install additional build dependencies:
```shell
sudo apt-get install default-jdk git python-dev unzip xz-utils
```
[resource-manager]: https://console.cloud.google.com/cloud-resource-manager
[modify-project]: https://cloud.google.com/billing/docs/how-to/modify-project
[cloud-sdk]: https://cloud.google.com/sdk/docs/
[install-bazel]: https://github.com/bazelbuild/bazel/blob/4.0.0/site/docs/install-ubuntu.md
## Build and deploy the project
1. Clone the source repo.
```shell
git clone https://github.com/googlecloudrobotics/core
cd core
```
1. Create application default credentials, which are used to deploy the cloud project and
authorize access to the cloud docker registry.
```shell
gcloud auth application-default login
gcloud auth configure-docker
```
1. Create a Cloud Robotics config in your project:
```shell
./deploy.sh set_config [PROJECT_ID]
```
You can keep the defaults for the other settings by hitting `ENTER`.
This command creates a file `config.sh` containing your choices and stores
in into a cloud-storage bucket named `[PROJECT_ID]-cloud-robotics-config`.
You can verify the settings using:
```shell
gcloud storage cat gs://[PROJECT_ID]-cloud-robotics-config/config.sh
```
1. Build the project. Depending on your computer and internet connection, it may take around 15 minutes.
```shell
bazel build //...
```
1. Deploy the cloud project.
```shell
./deploy.sh create [PROJECT_ID]
```
> **Known issue:**
> Sometimes, this command fails with an error message like
> `Error 403: The caller does not have permission`,
> `Error 403: Service ... not found or permission denied',
> `Bad status during token exchange: 503`, or
> `Error enabling service`.
> In these cases, wait for a minute and try again.
`deploy.sh` created a Kubernetes cluster using Google Kubernetes Engine and used Helm to install the Cloud Robotics Core components.
You can browse these components on the [Workloads dashboard](https://console.cloud.google.com/kubernetes/workload).
Alternatively, you can list them from the console on your workstation:
```console
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
cert-manager-xxx 1/1 Running 0 1m
nginx-ingress-xxx 1/1 Running 0 1m
oauth2-proxy-xxx 0/1 CrashLoopBackOff 4 1m
token-vendor-xxx 1/1 Running 0 1m
```
> **Note** Unless you already set up OAuth, the `oauth2-proxy` will show an error which we will ignore for now.
In addition to the cluster, `deploy.sh` also created:
* the [[PROJECT_ID]-robot Cloud Storage bucket](https://console.cloud.google.com/storage/browser), containing the scripts that connect robots to the cloud, and
* the [Identity & Access Management policies](https://console.cloud.google.com/iam-admin/iam) that authorize robots and humans to communicate with GCP.
With the project deployed, you're ready to [connect a robot to the cloud](connecting-robot.md).
## Update the project
To apply changes made in the source code, run:
```shell
./deploy.sh update [PROJECT_ID]
```
## Clean up
The following command will delete:
* the [cloud-robotics Kubernetes cluster](https://console.cloud.google.com/kubernetes/list)
This can be useful if the cluster is in a broken state.
Be careful with this invocation, since you'll have to redeploy the project and reconnect any robots afterwards.
```shell
./deploy.sh delete [PROJECT_ID]
```
If you want to completely shut down the project, see [the Resource Manager documentation](https://cloud.google.com/resource-manager/docs/creating-managing-projects#shutting_down_projects).
## What's next
* [Connecting a robot to the cloud](connecting-robot.md).
* [Setting up OAuth for web UIs](setting-up-oauth.md).
================================================
FILE: docs/how-to/deploying-grpc-service.md
================================================
# Deploying a gRPC service written in C++
Estimated time: 60 min
In this guide we will deploy a gRPC service written in C++ and deploy it to our Google Kubernetes Engine (GKE) cluster in the cloud in such a way that authentication is required for access. We will show how to access the service from the workstation and how to access it from code running in the robot's Kubernetes cluster.
## Prerequisites
* Completed the [Quickstart Guide](../quickstart.md), after which the GCP project is set up and `gcloud-sdk` and `kubectl` are installed and configured.
* `docker` is installed and configured on the workstation ([instructions](https://docs.docker.com/install/linux/docker-ce/ubuntu/)).
* `git` is installed on the workstation.
* For the last part of the guide: A robot that has been successfully [connected to the cloud](connecting-robot.md).
All files for this tutorial are located in
[docs/how-to/examples/greeter-service/](https://github.com/googlecloudrobotics/core/tree/master/docs/how-to/examples/greeter-service).
```shell
git clone https://github.com/googlecloudrobotics/core
cd core/docs/how-to/examples/greeter-service
```
Set your GCP project ID as an environment variable:
```shell
export PROJECT_ID=[YOUR_GCP_PROJECT_ID]
```
## Running gRPC server and client locally
We will use [gRPC's quickstart example](https://grpc.io/docs/quickstart/cpp.html) with small modifications.
If you like to learn more about gRPC in C++, follow their guide first.
The gRPC `helloworld.Greeter` service is defined in `proto/helloworld.proto`.
It accepts a `HelloRequest` containing a `name` and responds with a `HelloReply` containing a `message`.
The server is implemented in `server/server.cc` and the client is implemented `client/client.cc`. The client sends the request with `name: "world"` to the server which responds with `message: "Hello <name>"`.
In this tutorial, we build the server and client code inside Docker containers, so you don't need to install the gRPC library.
If you prefer, you can install the gRPC following [these instructions](https://github.com/grpc/grpc/blob/master/src/cpp/README.md) and build the server and client locally using the provided `Makefile`.
Make sure the Docker daemon is running and your user has the necessary privileges:
```shell
docker run --rm hello-world
```
If this command fails, make sure Docker is installed according to the [installation instructions](https://docs.docker.com/install/linux/docker-ce/ubuntu/).
The Docker image for the server is configured in `server/Dockerfile`:
[embedmd]:# (examples/greeter-service/server/Dockerfile dockerfile)
```dockerfile
FROM grpc/cxx:1.12.0
WORKDIR /data
COPY server/server.cc ./server/
COPY proto/helloworld.proto ./proto/
COPY Makefile ./
RUN make greeter-server && make clean
CMD ["./greeter-server"]
```
We use the [grpc/cxx](https://hub.docker.com/r/grpc/cxx) Docker image which contains all the build tools and libraries (`g++`, `make`, `protoc`, and `grpc`) we need to build the `greeter-server` binary.
The Docker image for the client is configured in `client/Dockerfile` which builds the `greeter-client` from `client/client.cc`.
To build the Docker images, run:
```shell
docker build -t greeter-server -f server/Dockerfile .
docker build -t greeter-client -f client/Dockerfile .
```
> **Note**
> The docker files are in the subfolders `greeter-server/server/` and `greeter-server/client`, but the docker command must be called from `greeter-server/` to include the files which are shared between the server and the client.
You should now have an image tagged `greeter-server` and one tagged `greeter-client` in your local registry:
```shell
docker images | grep greeter
```
To run the server locally, the container's port 50051, which specified as gRPC port in `server.cc`, has to be published to your machine with the flag `-p 50051:50051`:
```shell
docker run --rm -p 50051:50051 --name greeter-server greeter-server
```
In another console run the client container.
The flag `--network=host` tells the container to use your workstation's network stack which allows the client to connect to `localhost`.
```shell
docker run --rm --network=host greeter-client ./greeter-client localhost
```
You should see `Greeter received: Hello world` in the client's output and `Received request: name: "world"` in the server's output. You can also send your own name in the gRPC request to the server, try:
```shell
docker run --rm --network=host greeter-client \
./greeter-client localhost $USER
```
You can stop the server from another terminal by running:
```shell
docker stop greeter-server
```
## Uploading the Docker image to the cloud
In order to be able to run the server as a container in our cloud cluster, we need to upload the Docker image to our GCP project's private [container registry](https://cloud.google.com/container-registry/docs/pushing-and-pulling).
Enable the Docker credential helper:
```shell
gcloud auth configure-docker
```
Tag the image and push it to the registry:
```shell
docker tag greeter-server gcr.io/$PROJECT_ID/greeter-server
docker push gcr.io/$PROJECT_ID/greeter-server
```
The image should now show up in the [container registry](https://console.cloud.google.com/gcr).
## Deploying the service in the cloud using Kubernetes
Run the following command to create `greeter-server.yaml` using the provided template:
```shell
cat greeter-server.yaml.tmpl | envsubst >greeter-server.yaml
```
This file contains the information needed by Kubernetes to run the gRPC service in our cloud cluster.
The three resources, Ingress, Service, and Deployment, are explained in the [deploying a service tutorial](deploying-service.md).
In contrast to the other tutorial, the Ingress tells nginx to forward incoming requests to a gRPC backend.
[embedmd]:# (examples/greeter-service/greeter-server.yaml.tmpl yaml /^/ /---/)
```yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: greeter-server-ingress
annotations:
nginx.ingress.kubernetes.io/backend-protocol: GRPC
nginx.ingress.kubernetes.io/auth-url: "http://token-vendor.default.svc.cluster.local/apis/core.token-vendor/v1/token.verify?robots=true"
spec:
ingressClassName: nginx
rules:
- host: "www.endpoints.${PROJECT_ID}.cloud.goog"
http:
paths: # must match the namespace and service name in the proto
- path: /helloworld.Greeter/
pathType: Prefix
backend:
service:
name: greeter-server-service
# must match the port used in server.cc
port:
number: 50051
---
```
Make sure that `kubectl` points to the correct GCP project:
```shell
kubectl config get-contexts
```
If the correct cluster is not marked with an asterisk in the output, you can switch contexts with `kubectl config use-context [...]`.)
Then deploy by applying the configuration:
```shell
kubectl apply -f greeter-server.yaml
```
You can explore the various resources that were created on your cluster as a result of this command in the [GKE Console](https://console.cloud.google.com/kubernetes/workload) or with `kubectl`, e.g.:
```shell
kubectl get pods
```
The resulting list should contain a running pod with a name like `greeter-server-xxxxxxxxxx-xxxxx`.
## Redeploying after a change
For convenience, `deploy.sh` provides some commands to create, delete, and update the service.
If you make changes to `greeter-server.yaml.tmpl`, all you have to do is run:
```shell
./deploy.sh update_config
```
If you make changes to `server.cc`, you need to run:
```shell
./deploy.sh update_server
```
This builds, tags, and pushes the Docker image, and then forces a redeployment of the image by calling `kubectl delete pod -l 'app=greeter-server-app'`.
It also updates the resource definitions, so you don't have to run `./deploy.sh update_config` if you made changes to both files.
## Accessing the API
In `client/client.cc` we use `grpc::InsecureChannelCredentials()` when talking to `localhost` while we use `grpc::GoogleDefaultCredentials()` when talking to any other address.
SSL authentication with credentials from the user or robot are necessary when talking to the `greeter-server` in the Cloud Robotics project.
[embedmd]:# (examples/greeter-service/client/client.cc c++ /^ +if.*localhost/ /^ +}$/)
```c++
if (grpc_endpoint.find("localhost:") == 0 ||
grpc_endpoint.find("127.0.0.1:") == 0) {
channel_creds = grpc::InsecureChannelCredentials();
} else {
channel_creds = grpc::GoogleDefaultCredentials();
}
```
Let's try to access our server.
We have to connect to the nginx ingress which is hosted on `www.endpoints.$PROJECT_ID.cloud.goog:443`.
To ensure we have valid credentials to talk to nginx we have to mount our `~/.config` folder in the container.
```shell
docker run --rm -v ~/.config:/root/.config greeter-client \
./greeter-client www.endpoints.$PROJECT_ID.cloud.goog:443 workstation
```
Recall that when running `./greeter-server` on your workstation you were able to see the server's log output upon receiving a request.
This log output is also recorded when the server is running in the cloud cluster. To inspect it, run:
```shell
kubectl logs -l 'app=greeter-server-app'
```
Or go to the [GKE Console](https://console.cloud.google.com/kubernetes/workload), select the `greeter-server` workload and click on "Container logs".
## Accessing the API from the robot
In order to run `greeter-client` on the robot's Kubernetes cluster, we again package it as a Docker image and push it to our container registry, to which the robot also has access.
Our deploy script offers a command to build, tag, and push the image to the cloud registry, like we did with the server container:
```shell
./deploy.sh push_client
```
And finally, to execute the script, SSH into robot and run:
```shell
export PROJECT_ID=[YOUR_GCP_PROJECT_ID]
docker pull grpc/cxx:1.12.0 # This may take several minutes, depending on WiFi connection
kubectl run -ti --rm --restart=Never --image=gcr.io/$PROJECT_ID/greeter-client greeter-client \
-- ./greeter-client www.endpoints.$PROJECT_ID.cloud.goog:443 robot
```
You should see the server's answer `Hello robot`.
Two things are noteworthy:
* The `greeter-client` Docker image was pulled from the container registry without the need for additional credentials. This worked because there is a periodical job running on the robot's Kubernetes cluster that refreshes the GCR credentials. Run `kubectl get pods` on the robot and you will see pod names that start with `gcr-credential-refresher`.
* `grpc::GoogleDefaultCredentials()` in the client's code automatically obtained credentials that allowed the robot to access the cloud cluster. This worked because the the local Metadata Server obtains access tokens for the robot in the background.
## Cleaning up
In order to stop the service in the cloud cluster and revert the configuration changes, run:
```shell
./deploy.sh delete
```
================================================
FILE: docs/how-to/deploying-service.md
================================================
# Deploying a service to the cloud cluster
Estimated time: 60 min
In this guide we will write a HTTP service in Python and deploy it to our Google Kubernetes Engine (GKE) cluster in the cloud in such a way that authentication is required for access. We will show how to access the service from the workstation and how to access it from code running in the robot's Kubernetes cluster.
## Prerequisites
* Completed the [Quickstart Guide](../quickstart.md), after which the GCP project is set up and `gcloud-sdk` and `kubectl` are installed and configured.
* `docker` is installed and configured on the workstation ([instructions](https://docs.docker.com/install/linux/docker-ce/ubuntu/)).
* `python3`, `python3-pip`, and `curl` are installed on the workstation.
* For the last part of the guide: A robot that has been successfully [connected to the cloud](connecting-robot.md).
Create a directory for the code examples of this guide, e.g.:
```shell
mkdir hello-service
cd hello-service
```
Set your GCP project ID as an environment variable:
```shell
export PROJECT_ID=[YOUR_GCP_PROJECT_ID]
```
All files created in this tutorial can be found in
[docs/how-to/examples/hello-service/](https://github.com/googlecloudrobotics/core/tree/master/docs/how-to/examples/hello-service).
If you download the files, you have to replace the placeholders `[PROJECT_ID]` with your GCP project ID:
```shell
sed -i "s/\[PROJECT_ID\]/$PROJECT_ID/g" client/client.py server/hello-server.yaml
```
## A simple HTTP server
Create a subdirectory for the server code:
```shell
mkdir server
cd server
```
Paste the following into a file called `server.py`:
[embedmd]:# (examples/hello-service/server/server.py python)
```python
from http import server
import signal
import sys
class MyRequestHandler(server.BaseHTTPRequestHandler):
def do_GET(self):
print('Received a request')
self.send_response(200)
self.send_header('Content-Type', 'text/plain')
self.end_headers()
self.wfile.write(b'Server says hello!\n')
def main():
# Terminate process when Kubernetes sends SIGTERM.
signal.signal(signal.SIGTERM, lambda *_: sys.exit(0))
server_address = ('', 8000)
httpd = server.HTTPServer(server_address, MyRequestHandler)
httpd.serve_forever()
if __name__ == '__main__':
main()
```
This Python program implements a server that listens on port 8000 for incoming HTTP GET requests. When such a request is received, it prints a line to stdout and responds to the request with a short message.
You can try it out with:
```shell
python server.py
```
If you see `ImportError: No module named http`, you are most likely using Python 2.x; try `python3` instead of `python`.)
Then, from another terminal on the same workstation, run:
```shell
curl -i http://localhost:8000
```
You should see the headers indicating that the request was successful (`200 OK`) and the server's response message. You can also try entering `localhost:8000` in your browser's address bar.
## Dockerizing the service
Next, to prepare our Python program for deployment in the cloud, we package it as a Docker image.
Make sure that the docker daemon is running and that your user has the necessary privileges:
```shell
docker run --rm hello-world
```
If this command fails, make sure Docker is installed according to the [installation instructions](https://docs.docker.com/install/linux/docker-ce/ubuntu/).
In the same directory as `server.py`, create a `Dockerfile` with the following contents:
[embedmd]:# (examples/hello-service/server/Dockerfile dockerfile)
```dockerfile
FROM python:alpine
WORKDIR /data
COPY server.py ./
CMD [ "python", "-u", "./server.py" ]
```
(Note: the `-u` option disables line-buffering; Python's line-buffering can prevent output from appearing immediately in the Docker logs.)
To build the Docker image, run:
```shell
docker build -t hello-server .
```
You should now have an image tagged `hello-server` in your local registry:
```shell
docker images | grep hello-server
```
It can be run locally with:
```shell
docker run -ti --rm -p 8000:8000 hello-server
```
You should now be able to send requests to the server with `curl` as before.
## Uploading the Docker image to the cloud
In order to be able to run the server as a container in our cloud cluster, we need to upload the Docker image to our GCP project's private [container registry](https://cloud.google.com/container-registry/docs/pushing-and-pulling).
Enable the Docker credential helper:
```shell
gcloud auth configure-docker
```
Tag the image and push it to the registry:
```shell
docker tag hello-server gcr.io/$PROJECT_ID/hello-server
docker push gcr.io/$PROJECT_ID/hello-server
```
The image should now show up in the [Container Registry](https://console.cloud.google.com/gcr).
## Deploying the service in the cloud using Kubernetes
Create a file called `hello-server.yaml` with the following contents:
[embedmd]:# (examples/hello-service/server/hello-server.yaml yaml)
```yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: hello-server-ingress
annotations:
nginx.ingress.kubernetes.io/auth-url: "http://token-vendor.default.svc.cluster.local/apis/core.token-vendor/v1/token.verify?robots=true"
spec:
ingressClassName: nginx
rules:
- host: www.endpoints.[PROJECT_ID].cloud.goog
http:
paths:
- path: /apis/hello-server
pathType: Prefix
backend:
service:
name: hello-server-service
port:
number: 8000
---
apiVersion: v1
kind: Service
metadata:
name: hello-server-service
spec:
ports:
- name: hello-server-port
port: 8000
# the selector is used to link pods to services
selector:
app: hello-server-app
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: hello-server
spec:
# all pods matching this selector belong to this deployment
selector:
matchLabels:
app: hello-server-app
template:
metadata:
# the other side of the link between services and pods
labels:
app: hello-server-app
spec:
containers:
- name: hello-server
image: gcr.io/[PROJECT_ID]/hello-server:latest
ports:
# must match the port of the service
- containerPort: 8000
```
This file contains the information needed by Kubernetes to run our HTTP service in our cloud cluster. In the following, we will go over it bit by bit assuming basic familiarity with the [YAML format](https://en.wikipedia.org/wiki/YAML).
We define three Kubernetes resources:
* The *Ingress* contains rules that tell our cluster's nginx (the HTTP server that handles all incoming traffic) which incoming requests to forward to our service.
* The *Service* defines how our service is exposed within the cluster.
* The *Deployment* describes the Docker container to run.
Metadata, labels, and selectors are used to tie the three resources together.
A detailed explanation of the Kubernetes resources is out of scope for this guide, check out the [Kubernetes docs](https://kubernetes.io/docs/home/) to get started. There are a few points worth mentioning, though:
* In the Ingress and Deployment, don't forget to replace `[PROJECT_ID]` with your GCP project ID.
* In the Ingress, there is an annotation with key `nginx.ingress.kubernetes.io/auth-url`. This tells our cluster's nginx to check the authorization of each request before forwarding it to the `hello-server`. The value `http://token-vendor...` is the cluster-internal DNS address of a token verifier service that is running in the cluster as part of the Cloud Robotics Core platform.
* The Ingress rules specify that our hello-server will be reachable at `https://www.endpoints.[PROJECT_ID].cloud.goog/apis/hello-server`.
* The Deployment contains the full reference of the Docker image that we pushed to the Container Registry in the previous section.
Make sure that `kubectl` points to the correct GCP project:
```shell
kubectl config get-contexts
```
If the correct cluster is not marked with an asterisk in the output, you can switch to it with `kubectl config use-context [...]`.)
Then deploy by applying the configuration:
```shell
kubectl apply -f hello-server.yaml
```
You can explore the various resources that were created on your cluster as a result of this command in the [GKE Console](https://console.cloud.google.com/kubernetes/workload) or with `kubectl`, e.g.:
```shell
kubectl get pods
```
The resulting list should contain a running pod with a name like `hello-server-xxxxxxxxxx-xxxxx`.
## Redeploying after a change
If you make a change to `server.py`, you need to rebuild and push the Docker image:
```shell
docker build -t hello-server .
docker tag hello-server gcr.io/$PROJECT_ID/hello-server
docker push gcr.io/$PROJECT_ID/hello-server
```
The easiest way to get Kubernetes to restart the workload with the latest version of the container is to delete the pod:
```shell
kubectl delete pod -l 'app=hello-server-app'
```
Kubernetes will automatically pull the newest image and recreate the pod.
If you make a change to `hello-server.yaml`, all you have to do is apply it again:
```shell
kubectl apply -f hello-server.yaml
```
## Accessing the API
Let's try to access our server as we did before:
```shell
curl -i https://www.endpoints.$PROJECT_ID.cloud.goog/apis/hello-server
```
This should result in a `401 Unauthorized` error because we did not supply any authorization information with the request.
(Note: If you comment out the `auth-url` annotation in the Ingress definition and reapply it, this request will succeed.)
We can, however, easily obtain credentials from `gcloud` and attach them to our request by means of an "Authorization" header:
```shell
token=$(gcloud auth application-default print-access-token)
curl -i -H "Authorization: Bearer $token" https://www.endpoints.$PROJECT_ID.cloud.goog/apis/hello-server
```
If this command fails because "Application Default Credentials are not available", you need to first run:
```shell
gcloud auth application-default login --project=$PROJECT_ID
```
And follow the instructions in your browser.
Recall that when running `server.py` on your workstation you were able to see the server's log output upon receiving a request. This log output is also recorded when the server is running in the cloud cluster. To inspect it, run:
```shell
kubectl logs -l 'app=hello-server-app'
```
Or go to the [GKE Console](https://console.cloud.google.com/kubernetes/workload), select the `hello-server` workload and click on "Container logs".
Next, let's access the API from some Python code. Eventually, we will build another Docker image from this code, so it needs to live in a separate directory:
```shell
cd ..
mkdir client
cd client
```
Get some dependencies:
```shell
pip3 install --upgrade google-auth requests
```
(Depending on your local installation, you might have to use `pip3`.)
Create `client.py` with the following contents:
[embedmd]:# (examples/hello-service/client/client.py python)
```python
import google.auth
import google.auth.transport.requests as requests
credentials, project_id = google.auth.default()
authed_session = requests.AuthorizedSession(credentials)
response = authed_session.request(
"GET", "https://www.endpoints.[PROJECT_ID].cloud.goog/apis/hello-server")
print(response.status_code, response.reason, response.text)
```
Replace `[PROJECT_ID]` with your GCP project ID.
This script:
* uses [`google-auth`](https://google-auth.readthedocs.io/en/latest/user-guide.html) to obtain application default credentials (just as we previously did with the `gcloud` CLI),
* uses the [`requests`](http://docs.python-requests.org/en/stable/) library to perform an authenticated request to our API,
* prints the response to stdout.
Try it out:
```shell
python3 client.py
```
You will get a warning about using end user credentials. You can safely ignore this warning; we will eventually be using a robot's credentials.)
## Accessing the API from the robot
In order to run this script on the robot's Kubernetes cluster, we again package it as a Docker image and push it to our container registry, to which the robot also has access.
Create a `Dockerfile` containing:
[embedmd]:# (examples/hello-service/client/Dockerfile dockerfile)
```dockerfile
FROM python:alpine
RUN pip install --no-cache-dir google-auth requests
WORKDIR /data
COPY client.py ./
CMD [ "python", "-u", "./client.py" ]
```
Build, tag, and push the image:
```shell
docker build -t hello-client .
docker tag hello-client gcr.io/$PROJECT_ID/hello-client
docker push gcr.io/$PROJECT_ID/hello-client
```
And finally, to execute the script, SSH into robot and run:
```shell
kubectl run -ti --rm --restart=Never --image=gcr.io/$PROJECT_ID/hello-client hello-client
```
You should see the server's message.
Two things are noteworthy:
* The `hello-client` Docker image was pulled from the Container Registry without the need for additional credentials. This worked because there is a periodical job running on the robot's Kubernetes cluster that refreshes the GCR credentials. Run `kubectl get pods` on the robot and you will see pod names that start with `gcr-credential-refresher`.
* The `google.auth.default()` invocation in the Python code automatically obtained credentials that allowed the robot to access the cloud cluster. This worked because the `google-auth` library queried the local Metadata Server, which obtains access tokens for the robot in the background.
## Cleaning up
In order to stop the service in the cloud cluster and revert the configuration changes, change to the `server` directory and run:
```shell
kubectl delete -f hello-server.yaml
```
<!--
## What's next
There are a few inconvenient steps in this guide, e.g.:
* manually replacing the project ID in all source files or
* remotely logging in to the robot to start a workload.
This is where the app management layer comes in; it provides, among other capabilities:
* ways of bundling and parameterizing Kubernetes resources and
* remote management of Kubernetes resources/workloads on the robot.
Also note that both the server and the client side can be implemented with similar ease in other programming languages, such as [Go](https://github.com/googleapis/google-api-go-client).
-->
================================================
FILE: docs/how-to/examples/charge-service/Dockerfile
================================================
FROM python:alpine
WORKDIR /data
COPY server.py ./
CMD [ "python", "-u", "./server.py" ]
================================================
FILE: docs/how-to/examples/charge-service/charge-action.yaml
================================================
apiVersion: example.com/v1
kind: ChargeAction
metadata:
name: my-charge-action
================================================
FILE: docs/how-to/examples/charge-service/charge-controller.yaml
================================================
apiVersion: metacontroller.k8s.io/v1alpha1
kind: CompositeController
metadata:
name: charge-controller
spec:
generateSelector: true
parentResource:
apiVersion: example.com/v1
resource: chargeactions
resyncPeriodSeconds: 1
hooks:
sync:
webhook:
url: http://charge-controller.default:8000/sync
---
apiVersion: v1
kind: Service
metadata:
name: charge-controller
spec:
selector:
app: charge-controller
ports:
- port: 8000
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: charge-controller
spec:
replicas: 1
selector:
matchLabels:
app: charge-controller
template:
metadata:
labels:
app: charge-controller
spec:
containers:
- name: controller
image: gcr.io/[PROJECT_ID]/charge-controller
ports:
- containerPort: 8000
================================================
FILE: docs/how-to/examples/charge-service/charge-crd.yaml
================================================
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: chargeactions.example.com
annotations:
cr-syncer.cloudrobotics.com/spec-source: cloud
spec:
group: example.com
names:
kind: ChargeAction
plural: chargeactions
singular: chargeaction
scope: Namespaced
versions:
- name: v1
served: true
storage: true
subresources:
status: {}
schema:
openAPIV3Schema:
type: object
x-kubernetes-preserve-unknown-fields: true
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: cloud-robotics:cr-syncer:chartaction
labels:
cr-syncer.cloudrobotics.com/aggregate-to-robot-service: "true"
rules:
- apiGroups:
- example.com
resources:
- chargeactions
verbs:
- get
- list
- watch
- update
================================================
FILE: docs/how-to/examples/charge-service/server.py
================================================
from http.server import BaseHTTPRequestHandler, HTTPServer
import json
import signal
import sys
import time
import uuid
class Controller(BaseHTTPRequestHandler):
def sync(self, parent, children):
"""Actuate a ChargeAction custom resource.
The resource is actuated by using the Charge Service to send the robot to a
charger. While charging is in progress, the status of the resource is
updated to reflect the charge level.
Because the ChargeAction is handled by an external service, this controller
doesn't create any child resources, ie the `children` list is empty.
Args:
parent: The current ChargeAction resource.
children: Unused.
Returns:
A dict containing the latest status of the action, and an empty list of
children, eg:
{
"status": {
"state": "OK",
},
"children": [],
}
"""
# Get current status and copy to start building next status.
current_status = parent.get("status", None) or {}
desired_status = dict(current_status)
state = current_status.get("state", "CREATED")
if state == "CREATED":
# The ChargeAction has just been created. Use the external Charge Service
# to start charging. Store the request ID in the status so we can use it
# to check the state of the charge request.
request_id = self.charge_service.start_charging()
desired_status["state"] = "IN_PROGRESS"
desired_status["request_id"] = request_id
elif state == "IN_PROGRESS":
try:
# Get the progress of the charge request from the external service.
progress = self.charge_service.get_progress(
current_status["request_id"])
desired_status["charge_level_percent"] = progress
if progress == 100:
# Charging has completed.
desired_status["state"] = "OK"
except ValueError as e:
# The charge request was not found. This could be because the robot was
# restarted during a charge, and the request was forgotten.
desired_status["state"] = "ERROR"
desired_status["message"] = str(e)
elif state in ["OK", "CANCELLED", "ERROR"]:
# Terminal state, do nothing.
pass
else:
desired_status["state"] = "ERROR"
desired_status["message"] = "Unrecognized state: %r" % state
return {"status": desired_status, "children": []}
def do_POST(self):
"""Serve the sync() function as a JSON webhook."""
observed = json.loads(self.rfile.read(int(self.headers["content-length"])))
desired = self.sync(observed["parent"], observed["children"])
self.send_response(200)
self.send_header("Content-type", "application/json")
self.end_headers()
self.wfile.write(json.dumps(desired).encode('utf-8'))
class ChargeService(object):
"""ChargeService wraps an external API that send the robot to a charger.
For this example, it just fakes the charging process.
"""
SECONDS_FOR_FULL_CHARGE = 10
def __init__(self):
self._requests = {}
def start_charging(self):
request_id = str(uuid.uuid4())
self._requests[request_id] = time.time()
return request_id
def get_progress(self, request_id):
if request_id not in self._requests:
raise ValueError("invalid request ID")
charge_time = time.time() - self._requests[request_id]
if charge_time > self.SECONDS_FOR_FULL_CHARGE:
return 100
else:
return int(100 * (charge_time / self.SECONDS_FOR_FULL_CHARGE))
# Terminate process when Kubernetes sends SIGTERM.
signal.signal(signal.SIGTERM, lambda *_: sys.exit(0))
Controller.charge_service = ChargeService()
HTTPServer(("", 8000), Controller).serve_forever()
================================================
FILE: docs/how-to/examples/greeter-service/Makefile
================================================
#
# Copyright 2019 The Cloud Robotics 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.
#
HOST_SYSTEM = $(shell uname | cut -f 1 -d_)
SYSTEM ?= $(HOST_SYSTEM)
CXX = g++
CPPFLAGS += `pkg-config --cflags protobuf grpc`
CXXFLAGS += -std=c++11 -I .
ifeq ($(SYSTEM),Darwin)
LDFLAGS += -L/usr/local/lib `pkg-config --libs protobuf grpc++ grpc`\
-lgrpc++_reflection\
-ldl
else
LDFLAGS += -L/usr/local/lib `pkg-config --libs protobuf grpc++ grpc`\
-Wl,--no-as-needed -lgrpc++_reflection -Wl,--as-needed\
-ldl
endif
PROTOC = protoc
GRPC_CPP_PLUGIN = grpc_cpp_plugin
GRPC_CPP_PLUGIN_PATH ?= `which $(GRPC_CPP_PLUGIN)`
PROTOS_PATH = ./proto/
vpath %.proto $(PROTOS_PATH)
all: greeter-server greeter-client
greeter-server: helloworld.pb.o helloworld.grpc.pb.o server/server.o
$(CXX) $^ $(LDFLAGS) -o $@
greeter-client: helloworld.pb.o helloworld.grpc.pb.o client/client.o
$(CXX) $^ $(LDFLAGS) -o $@
.PRECIOUS: %.grpc.pb.cc
%.grpc.pb.cc: %.proto
$(PROTOC) -I $(PROTOS_PATH) --grpc_out=. --plugin=protoc-gen-grpc=$(GRPC_CPP_PLUGIN_PATH) $<
.PRECIOUS: %.pb.cc
%.pb.cc: %.proto
$(PROTOC) -I $(PROTOS_PATH) --cpp_out=. $<
clean:
rm -f *.o client/*.o server/*.o *.pb *.pb.cc *.pb.h
================================================
FILE: docs/how-to/examples/greeter-service/client/Dockerfile
================================================
FROM grpc/cxx:1.12.0
WORKDIR /data
COPY client/client.cc ./client/
COPY proto/helloworld.proto ./proto/
COPY Makefile ./
RUN make greeter-client && make clean
CMD ["./greeter-client"]
================================================
FILE: docs/how-to/examples/greeter-service/client/client.cc
================================================
/*
*
* Copyright 2019 The Cloud Robotics 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.
*
*/
#include <iostream>
#include <memory>
#include <string>
#include <grpcpp/grpcpp.h>
#include "helloworld.grpc.pb.h"
using grpc::Channel;
using grpc::ChannelCredentials;
using grpc::ClientContext;
using grpc::Status;
using helloworld::Greeter;
using helloworld::HelloReply;
using helloworld::HelloRequest;
class GreeterClient {
public:
GreeterClient(std::shared_ptr<Channel> channel)
: stub_(Greeter::NewStub(channel)) {}
// Assembles the client's payload, sends it and presents the response back
// from the server.
std::string SayHello(const std::string& user) {
// Data we are sending to the server.
HelloRequest request;
request.set_name(user);
// Container for the data we expect from the server.
HelloReply reply;
// Context for the client. It could be used to convey extra information to
// the server and/or tweak certain RPC behaviors.
ClientContext context;
// The actual RPC.
Status status = stub_->SayHello(&context, request, &reply);
// Act upon its status.
if (status.ok()) {
return reply.message();
} else {
std::cout << status.error_code() << ": " << status.error_message()
<< std::endl;
return "RPC failed";
}
}
private:
std::unique_ptr<Greeter::Stub> stub_;
};
int main(int argc, char** argv) {
if (argc < 2) {
const std::string client_path(argv[0]);
std::cout << "Usage:" << std::endl;
std::cout << " " << client_path << " <address[:port]> [<name>]"
<< std::endl;
std::cout << "Example:" << std::endl;
std::cout << " " << client_path
<< " www.endpoints.${PROJECT_ID}.cloud.goog:443" << std::endl;
return 0;
}
// The first parameter is the server's address, optionally containing the
// port.
std::string grpc_endpoint(argv[1]);
if (grpc_endpoint.find(":") == std::string::npos) {
// Set the default port of the server.
grpc_endpoint += ":50051";
}
// The optional second parameter is the name to be sent to the server.
std::string name("world");
if (argc >= 3) {
name = argv[2];
}
std::cout << "Sending request to " << grpc_endpoint << " ..." << std::endl;
// We are communicating via SSL to the endpoint service using the credentials
// of the user or robot running the client.
// We don't use credentials when connecting to localhost for testing.
std::shared_ptr<ChannelCredentials> channel_creds;
if (grpc_endpoint.find("localhost:") == 0 ||
grpc_endpoint.find("127.0.0.1:") == 0) {
channel_creds = grpc::InsecureChannelCredentials();
} else {
channel_creds = grpc::GoogleDefaultCredentials();
}
GreeterClient greeter(grpc::CreateChannel(grpc_endpoint, channel_creds));
std::string user(name);
std::string reply = greeter.SayHello(user);
std::cout << "Greeter received: " << reply << std::endl;
return 0;
}
================================================
FILE: docs/how-to/examples/greeter-service/deploy.sh
================================================
#!/bin/bash
#
# Copyright 2019 The Cloud Robotics 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.
set -o pipefail -o errexit
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
cd ${DIR}
function die {
echo "$1" >&2
exit 1
}
function push_image {
local target=$1
docker build -f "${target}/Dockerfile" -t "greeter-${target}" .
docker tag "greeter-${target}" "gcr.io/${PROJECT_ID}/greeter-${target}"
docker push "gcr.io/${PROJECT_ID}/greeter-${target}"
}
function create_config {
cat greeter-server.yaml.tmpl | envsubst >greeter-server.yaml
}
# public functions
function push_client {
push_image client
}
function update_config {
create_config
kubectl apply -f greeter-server.yaml
}
function update_server {
push_image server
kubectl delete pod -l 'app=greeter-server-app'
update_config
}
function create {
push_image server
push_client
update_config
}
function delete {
create_config
kubectl delete -f greeter-server.yaml
}
# main
if [[ -z ${PROJECT_ID} ]]; then
die "Set PROJECT_ID first: export PROJECT_ID=[GCP project id]"
fi
if [[ ! "$1" =~ ^(create|delete|update_config|update_server|push_client)$ ]]; then
die "Usage: $0 {create|delete|update_config|update_server|push_client}"
fi
# call arguments verbatim:
"$@"
================================================
FILE: docs/how-to/examples/greeter-service/greeter-server.yaml.tmpl
================================================
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: greeter-server-ingress
annotations:
nginx.ingress.kubernetes.io/backend-protocol: GRPC
nginx.ingress.kubernetes.io/auth-url: "http://token-vendor.default.svc.cluster.local/apis/core.token-vendor/v1/token.verify?robots=true"
spec:
ingressClassName: nginx
rules:
- host: "www.endpoints.${PROJECT_ID}.cloud.goog"
http:
paths: # must match the namespace and service name in the proto
- path: /helloworld.Greeter/
pathType: Prefix
backend:
service:
name: greeter-server-service
# must match the port used in server.cc
port:
number: 50051
---
apiVersion: v1
kind: Service
metadata:
name: greeter-server-service
spec:
ports:
- # optional descriptive name for the service port
name: grpc-port
# must match the service port specified in ingress
port: 50051
# the selector is used to link pods to services
selector:
app: greeter-server-app
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: greeter-server
spec:
replicas: 1
# all pods matching this selector belong to this deployment
selector:
matchLabels:
app: greeter-server-app
template:
metadata:
# the other side of the link between services and pods
labels:
app: greeter-server-app
spec:
containers:
- name: greeter-server
image: "gcr.io/${PROJECT_ID}/greeter-server:latest"
ports:
# must match the port of the service
- containerPort: 50051
================================================
FILE: docs/how-to/examples/greeter-service/proto/helloworld.proto
================================================
// Copyright 2019 The Cloud Robotics 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.
syntax = "proto3";
package helloworld;
// The greeting service definition.
service Greeter {
// Sends a greeting.
rpc SayHello (HelloRequest) returns (HelloReply) {}
}
// The request message containing the user's name.
message HelloRequest {
string name = 1;
}
// The response message containing the greetings.
message HelloReply {
string message = 1;
}
================================================
FILE: docs/how-to/examples/greeter-service/server/Dockerfile
================================================
FROM grpc/cxx:1.12.0
WORKDIR /data
COPY server/server.cc ./server/
COPY proto/helloworld.proto ./proto/
COPY Makefile ./
RUN make greeter-server && make clean
CMD ["./greeter-server"]
================================================
FILE: docs/how-to/examples/greeter-service/server/server.cc
================================================
/*
*
* Copyright 2019 The Cloud Robotics 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.
*
*/
#include <csignal>
#include <future>
#include <iostream>
#include <memory>
#include <string>
#include <thread>
#include <grpcpp/grpcpp.h>
#include "helloworld.grpc.pb.h"
using grpc::Server;
using grpc::ServerBuilder;
using grpc::ServerContext;
using grpc::Status;
using helloworld::Greeter;
using helloworld::HelloReply;
using helloworld::HelloRequest;
// The gRPC server is defined globally so that SIGTERM handler can shut it
// down when Kubernetes stops the process.
std::unique_ptr<Server> server;
// Logic and data behind the server's behavior.
class GreeterServiceImpl final : public Greeter::Service {
Status SayHello(ServerContext* context, const HelloRequest* request,
HelloReply* reply) override {
std::cout << "Received request: " << request->ShortDebugString()
<< std::endl;
std::string prefix("Hello ");
reply->set_message(prefix + request->name());
return Status::OK;
}
};
void RunServer() {
std::string server_address("0.0.0.0:50051");
GreeterServiceImpl service;
ServerBuilder builder;
// Listen on the given address without any authentication mechanism. Cloud
// Robotics Core ensures that clients are authenticated.
builder.AddListeningPort(server_address, grpc::InsecureServerCredentials());
// Register "service" as the instance through which we'll communicate with
// clients. In this case it corresponds to a *synchronous* service.
builder.RegisterService(&service);
// Finally assemble the server.
server = builder.BuildAndStart();
std::cout << "Server listening on " << server_address << std::endl;
std::signal(SIGTERM, [](int) {
// When SIGTERM is received, shutdown the gRPC server.
server->Shutdown();
});
// Wait for the server to shutdown.
server->Wait();
}
int main(int argc, char** argv) {
RunServer();
return 0;
}
================================================
FILE: docs/how-to/examples/hello-service/client/Dockerfile
================================================
FROM python:alpine
RUN pip install --no-cache-dir google-auth requests
WORKDIR /data
COPY client.py ./
CMD [ "python", "-u", "./client.py" ]
================================================
FILE: docs/how-to/examples/hello-service/client/client.py
================================================
import google.auth
import google.auth.transport.requests as requests
credentials, project_id = google.auth.default()
authed_session = requests.AuthorizedSession(credentials)
response = authed_session.request(
"GET", "https://www.endpoints.[PROJECT_ID].cloud.goog/apis/hello-server")
print(response.status_code, response.reason, response.text)
================================================
FILE: docs/how-to/examples/hello-service/server/Dockerfile
================================================
FROM python:alpine
WORKDIR /data
COPY server.py ./
CMD [ "python", "-u", "./server.py" ]
================================================
FILE: docs/how-to/examples/hello-service/server/hello-server.yaml
================================================
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: hello-server-ingress
annotations:
nginx.ingress.kubernetes.io/auth-url: "http://token-vendor.default.svc.cluster.local/apis/core.token-vendor/v1/token.verify?robots=true"
spec:
ingressClassName: nginx
rules:
- host: www.endpoints.[PROJECT_ID].cloud.goog
http:
paths:
- path: /apis/hello-server
pathType: Prefix
backend:
service:
name: hello-server-service
port:
number: 8000
---
apiVersion: v1
kind: Service
metadata:
name: hello-server-service
spec:
ports:
- name: hello-server-port
port: 8000
# the selector is used to link pods to services
selector:
app: hello-server-app
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: hello-server
spec:
# all pods matching this selector belong to this deployment
selector:
matchLabels:
app: hello-server-app
template:
metadata:
# the other side of the link between services and pods
labels:
app: hello-server-app
spec:
containers:
- name: hello-server
image: gcr.io/[PROJECT_ID]/hello-server:latest
ports:
# must match the port of the service
- containerPort: 8000
================================================
FILE: docs/how-to/examples/hello-service/server/server.py
================================================
from http import server
import signal
import sys
class MyRequestHandler(server.BaseHTTPRequestHandler):
def do_GET(self):
print('Received a request')
self.send_response(200)
self.send_header('Content-Type', 'text/plain')
self.end_headers()
self.wfile.write(b'Server says hello!\n')
def main():
# Terminate process when Kubernetes sends SIGTERM.
signal.signal(signal.SIGTERM, lambda *_: sys.exit(0))
server_address = ('', 8000)
httpd = server.HTTPServer(server_address, MyRequestHandler)
httpd.serve_forever()
if __name__ == '__main__':
main()
================================================
FILE: docs/how-to/running-ros-node.md
================================================
# Running a ROS node as a Kubernetes deployment
Estimated time: 10 min
The following instructions describe how to setup a Kubernetes cluster on a robot
running Ubuntu 20.04 and run a ROS node on it.
The installation script installs and configures:
* Docker
* A single-node Kubernetes cluster (packages: kubectl, kubeadm, kubelet)
Once you've done this, you can use Kubernetes to:
* Reduce downtime during updates with Kubernetes deployments
* Apply CPU, disk or memory quotas to individual processes
* Add additional compute nodes to the cluster, such as an Nvidia Jetson
* Use a network plugin to apply network access control
* Manage project configuration or sensitive secrets such as account credentials
For more details, refer to the [Kubernetes documentation](https://kubernetes.io/docs/home/).
## Installing the cluster on the robot
See <https://github.com/googlecloudrobotics/core/blob/main/docs/how-to/connecting-robot.md#installing-the-cluster-on-the-robot>.
## Run a ROS node with Kubernetes
If you're already using ROS on your robot, you can run a ROS node inside Kubernetes that will communicate with other nodes on the robot. If not, you can follow the [ROS tutorials](http://wiki.ros.org/ROS/Tutorials) to get started.
First, make sure you're running `roscore`. In another terminal, please run:
```shell
roscore
```
> **Caution:** If you have a more complicated ROS setup, such as a ROS master running on another machine, you might need to change `ROS_MASTER_URI` or `ROS_IP` in rostopic-echo.yaml.
You can run a ROS node by creating a Kubernetes Deployment object, and you can describe a Deployment in a YAML file.
For example, this YAML file describes a Deployment that runs `rostopic echo`.
Create file called `rostopic-echo.yaml` with the following contents:
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: rostopic-echo
spec:
selector:
matchLabels:
app: rostopic-echo
template:
metadata:
labels:
app: rostopic-echo
spec:
containers:
- name: rostopic-echo
image: ros:melodic-ros-core
args:
- rostopic
- echo
- chatter
env:
- name: ROS_MASTER_URI
value: http://192.168.9.1:11311
- name: ROS_IP
value: 192.168.9.1
hostNetwork: true
```
> **Note:** For simplicity, this example uses `hostNetwork: true` to disable network isolation. Advanced users can disable host networking to improve security. For more information, see the networking documentation for <a href="http://wiki.ros.org/ROS/NetworkSetup">Docker</a>, <a href="https://kubernetes.io/docs/concepts/cluster-administration/networking/">Kubernetes</a> and <a href="http://wiki.ros.org/ROS/NetworkSetup">ROS</a>.
After creating `rostopic-echo.yaml`, use `kubectl` to apply it to your cluster:
```shell
kubectl apply -f rostopic-echo.yaml
```
Depending on your internet connection, it will take a minute or so to download the Docker image. Wait until you see `Running`:
```console
$ watch kubectl get pods -l app=rostopic-echo
NAMESPACE NAME READY STATUS RESTARTS AGE
default rostopic-echo-576cbf47c7-dtlc6 1/1 Running 0 1m
```
Now, publish a ROS message and check that it was received inside Kubernetes:
```console
$ rostopic pub -1 chatter std_msgs/String "Hello, world"
$ kubectl logs -l app=rostopic-echo
data: "Hello, world"
---
```
Kubernetes will keep this node running until you delete the deployment:
```shell
kubectl delete -f rostopic-echo.yaml
```
================================================
FILE: docs/how-to/setting-up-oauth.md
================================================
# Setting up OAuth for web UIs
Estimated time: 5 min
When a user loads a web UI hosted in the cloud Kubernetes cluster, the server has to authenticate them before allowing them to use the service.
To enable this, you'll need to set up OAuth with the Cloud Console.
Once you've completed these steps, you'll be able to access services with web UIs, such as [Grafana](https://grafana.com/).
If you haven't already, complete the [Quickstart Guide](../quickstart.md) or [Deploy Cloud Robotics Core from sources](deploy-from-sources.md) to set up your GCP project.
## Create OAuth credentials
1. Open the [cloud console](https://console.cloud.google.com/) and ensure that
your cloud project is selected in the project selector dropdown at the top.
1. Configure the OAuth consent screen: [APIs & Services → Credentials → OAuth consent screen](https://console.cloud.google.com/apis/credentials/consent).
* User Type: Internal
* Application name: My Cloud Robotics Application
* Support email: *your email address*
* Add `[PROJECT_ID].cloud.goog` to Authorized domains (where `[PROJECT_ID]` is your GCP project ID).
* Leave the other fields blank.
1. Create an OAuth client ID: [APIs & Services → Credentials → Create credentials → OAuth client ID](https://console.cloud.google.com/apis/credentials/oauthclient).
* Application type: Web application
* Restrictions → Authorized JavaScript origins:<br/>
`https://www.endpoints.[PROJECT_ID].cloud.goog`
* Restrictions → Authorized redirect URIs: <br/>
`https://www.endpoints.[PROJECT_ID].cloud.goog/oauth2/callback`
* Click "Create".
You'll see a dialog containing the client ID and secret which we will add to your `config.sh` next.
## Update your config and redeploy
1. update your `config.sh` in the Google Cloud Storage bucket:
```shell
curl -fS "https://storage.googleapis.com/cloud-robotics-releases/run-install.sh" >run-install.sh
bash ./run-install.sh $PROJECT_ID --set-oauth
```
Enter the OAuth client ID and secret from the previous step when asked.
1. Update your cloud project:
```shell
bash ./run-install.sh $PROJECT_ID
```
After the update has been deployed, OAuth is enabled in your cloud project.
Verify that `oauth2-proxy` is running now:
```console
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
...
oauth2-proxy-xxx 1/1 Running 0 1m
```
## Try it out
Open a web browser and visit `https://www.endpoints.[PROJECT_ID].cloud.goog/grafana/dashboards`, replacing `[PROJECT_ID]` with your GCP project ID.
You'll be prompted to log in with your Google account, after which you'll see a list of dashboards.
Try selecting "Kubernetes Capacity Planning" to see the resource usage of the Kubernetes cluster.
================================================
FILE: docs/how-to/using-cloud-storage.md
================================================
# Using Cloud Storage from a robot
Estimated time: 20 minutes
This page describes a simple Cloud Storage transaction that demonstrates how Google Cloud APIs can be accessed without additional authentication configuration from within the robot's Kubernetes cluster.
Normally, to access a private Cloud Storage bucket from a robot, you'd need to manage a service account for the robot through Identity & Access Management (IAM). Cloud Robotics handles the robot's identity for you, so you can connect securely without additional configuration.
1. If you haven't already, complete the [Connecting a robot to the cloud](connecting-robot.md) steps.
1. Choose a name for the Cloud Storage bucket.
In the course of this guide, the robot will upload a file into a private bucket. The bucket namespace is global, so we must take care to choose a bucket name that is not in use yet by any other user of GCP. See also the [bucket naming requirements](https://cloud.google.com/storage/docs/naming), and [best practices](https://cloud.google.com/storage/docs/best-practices#naming).
For this guide we will assume a bucket name like `robot-hello-world-dc1bb474`, where the part after the last dash is a random hexadecimal number. You can generate your own unique bucket name with the command
```shell
echo robot-hello-world-$(tr -dc 'a-f0-9' < /dev/urandom | head -c8)
```
Note: If the bucket name is already in use, creating the bucket in the next step will fail. In this case, choose a different bucket name.
1. Create the Cloud Storage bucket.
On your workstation, run:
```shell
gcloud storage buckets create gs://[BUCKET_NAME]
```
Replace `[BUCKET_NAME]` with the name of the bucket you created, e.g., `robot-hello-world-dc1bb474`.
`gcloud storage` contains the sub-commands for accessing Cloud Storage, it is part of the `gcloud-sdk` package.
Note that the bucket is not publicly writable, as can be verified in the [Cloud Storage browser](https://console.cloud.google.com/storage/browser).
1. Drop a file into the bucket from the robot.
On the robot, run:
```console
docker pull python:alpine
kubectl run python --restart=Never --rm -ti --image=python:alpine -- /bin/sh
# apk add gcc musl-dev libffi-dev
# pip3 install google-cloud-storage
# python3
>>> from google.cloud import storage
>>> client = storage.Client()
>>> bucket = client.bucket("[BUCKET_NAME]")
>>> buck
gitextract_mwr70acn/
├── .bazelignore
├── .bazelrc
├── .bazelversion
├── .dockerignore
├── .editorconfig
├── .github/
│ ├── ci/
│ │ ├── .bazelrc
│ │ ├── Dockerfile.integration-test-image
│ │ ├── common.sh
│ │ ├── deploy_navtest.sh
│ │ ├── deploy_navtest_cloudbuild.yaml
│ │ ├── deployments/
│ │ │ ├── robco-integration-test/
│ │ │ │ ├── config.sh
│ │ │ │ └── kubernetes/
│ │ │ │ └── k8s-relay-rollout.yaml
│ │ │ └── robco-navtest/
│ │ │ └── config.sh
│ │ ├── integration_test.sh
│ │ ├── integration_test_cloudbuild.yaml
│ │ ├── integration_test_image_builder.sh
│ │ ├── presubmit.sh
│ │ └── release_binary.sh
│ ├── dependabot.yml
│ └── workflows/
│ ├── check-bazel.yml
│ ├── postsubmit.yml
│ ├── presubmit.yml
│ └── release.yml
├── .gitignore
├── .pep8
├── BUILD.bazel
├── CONTRIBUTING.md
├── LICENSE
├── METADATA
├── MODULE.bazel
├── README.md
├── bazel/
│ ├── BUILD.bazel
│ ├── BUILD.sysroot
│ ├── app.bzl
│ ├── app_chart.bzl
│ ├── build_rules/
│ │ ├── app_chart/
│ │ │ ├── BUILD.bazel
│ │ │ ├── Chart.yaml.template
│ │ │ ├── cache_gcr_credentials.bzl
│ │ │ ├── cache_gcr_credentials.sh.tpl
│ │ │ ├── push_all.bzl
│ │ │ ├── push_all.sh.tpl
│ │ │ ├── run_parallel.bzl
│ │ │ ├── run_parallel.sh.tpl
│ │ │ ├── values-cloud.yaml
│ │ │ └── values-robot.yaml
│ │ ├── copy.bzl
│ │ ├── helm_chart.bzl
│ │ └── helm_template.bzl
│ ├── container_push.bzl
│ └── debug_repository.bzl
├── config.sh.tmpl
├── current_versions.txt
├── deploy.sh
├── docs/
│ ├── .gitignore
│ ├── _config.yml
│ ├── concepts/
│ │ ├── app-management.md
│ │ ├── config.md
│ │ ├── device_identity.md
│ │ └── federation.md
│ ├── developers/
│ │ └── debug-auth.md
│ ├── how-to/
│ │ ├── connecting-robot.md
│ │ ├── creating-declarative-api.md
│ │ ├── deploy-from-sources.md
│ │ ├── deploying-grpc-service.md
│ │ ├── deploying-service.md
│ │ ├── examples/
│ │ │ ├── charge-service/
│ │ │ │ ├── Dockerfile
│ │ │ │ ├── charge-action.yaml
│ │ │ │ ├── charge-controller.yaml
│ │ │ │ ├── charge-crd.yaml
│ │ │ │ └── server.py
│ │ │ ├── greeter-service/
│ │ │ │ ├── Makefile
│ │ │ │ ├── client/
│ │ │ │ │ ├── Dockerfile
│ │ │ │ │ └── client.cc
│ │ │ │ ├── deploy.sh
│ │ │ │ ├── greeter-server.yaml.tmpl
│ │ │ │ ├── proto/
│ │ │ │ │ └── helloworld.proto
│ │ │ │ └── server/
│ │ │ │ ├── Dockerfile
│ │ │ │ └── server.cc
│ │ │ └── hello-service/
│ │ │ ├── client/
│ │ │ │ ├── Dockerfile
│ │ │ │ └── client.py
│ │ │ └── server/
│ │ │ ├── Dockerfile
│ │ │ ├── hello-server.yaml
│ │ │ └── server.py
│ │ ├── running-ros-node.md
│ │ ├── setting-up-oauth.md
│ │ └── using-cloud-storage.md
│ ├── index.md
│ ├── overview.md
│ └── quickstart.md
├── new_versions.txt
├── non_module_deps.bzl
├── nvchecker.toml
├── scripts/
│ ├── BUILD.bazel
│ ├── backup_robots.sh
│ ├── check-images.sh
│ ├── common.sh
│ ├── config.sh
│ ├── include-config.sh
│ ├── migrate.sh
│ ├── pre-commit
│ ├── robot-sim.sh
│ └── set-config.sh
├── src/
│ ├── .gitignore
│ ├── BUILD.bazel
│ ├── README.md
│ ├── app_charts/
│ │ ├── BUILD.bazel
│ │ ├── README.md
│ │ ├── akri/
│ │ │ ├── BUILD.bazel
│ │ │ ├── akri-robot.values.yaml
│ │ │ ├── robot/
│ │ │ │ └── akri.yaml
│ │ │ └── values-robot.yaml
│ │ ├── base/
│ │ │ ├── BUILD.bazel
│ │ │ ├── README.md
│ │ │ ├── app_management_test.sh
│ │ │ ├── cert-manager-cloud.values.yaml
│ │ │ ├── cert-manager-google-cas-issuer-cloud.values.yaml
│ │ │ ├── cert-manager-robot.values.yaml
│ │ │ ├── cloud/
│ │ │ │ ├── app-management-policy.yaml
│ │ │ │ ├── app-management.yaml
│ │ │ │ ├── apps-crd.yaml
│ │ │ │ ├── cert-ingress.yaml
│ │ │ │ ├── cert-manager-certificates.yaml
│ │ │ │ ├── cert-manager-google-cas-issuer.yaml
│ │ │ │ ├── cert-manager-issuers.yaml
│ │ │ │ ├── cert-manager.yaml
│ │ │ │ ├── cr-syncer-auth-webhook.yaml
│ │ │ │ ├── cr-syncer-policy.yaml
│ │ │ │ ├── domain-redirect.yaml
│ │ │ │ ├── fluentd-metrics.yaml
│ │ │ │ ├── kubernetes-api.yaml
│ │ │ │ ├── namespace.yaml
│ │ │ │ ├── nginx-ingress-controller-policy.yaml
│ │ │ │ ├── nginx-ingress-controller.yaml
│ │ │ │ ├── oauth2-proxy.yaml
│ │ │ │ ├── registry-crd.yaml
│ │ │ │ ├── registry-policy.yaml
│ │ │ │ ├── relay-dashboards.yaml
│ │ │ │ ├── token-vendor-app-fwd.yaml
│ │ │ │ └── token-vendor-rollout.yaml
│ │ │ ├── fluent-bit-helm.sh
│ │ │ ├── fluent-bit-values.yaml
│ │ │ ├── relay-dashboard.json
│ │ │ ├── robot/
│ │ │ │ ├── app-management.yaml
│ │ │ │ ├── cert-manager-certificates.yaml
│ │ │ │ ├── cert-manager-issuers.yaml
│ │ │ │ ├── cert-manager.yaml
│ │ │ │ ├── cr-syncer.yaml
│ │ │ │ ├── fluent-bit.yaml
│ │ │ │ ├── fluentd-gcp-addon.yaml
│ │ │ │ ├── fluentd-metrics.yaml
│ │ │ │ ├── gcr-credential-refresher.yaml
│ │ │ │ └── metadata-server.yaml
│ │ │ ├── values-cloud.yaml
│ │ │ └── values-robot.yaml
│ │ ├── k8s-relay/
│ │ │ ├── BUILD.bazel
│ │ │ ├── cloud/
│ │ │ │ ├── ingress.yaml
│ │ │ │ ├── kubernetes-relay-server.yaml
│ │ │ │ ├── service-monitor.yaml
│ │ │ │ └── service.yaml
│ │ │ ├── robot/
│ │ │ │ └── kubernetes-relay-client.yaml
│ │ │ ├── values-cloud.yaml
│ │ │ └── values-robot.yaml
│ │ ├── mission-crd/
│ │ │ ├── BUILD.bazel
│ │ │ ├── mission_crd.yaml
│ │ │ └── values.yaml
│ │ ├── platform-apps/
│ │ │ ├── BUILD.bazel
│ │ │ └── values.yaml
│ │ ├── prometheus/
│ │ │ ├── BUILD.bazel
│ │ │ ├── README.md
│ │ │ ├── cloud/
│ │ │ │ ├── app.yaml
│ │ │ │ ├── base-alerts.yaml
│ │ │ │ ├── federation-service-monitor.yaml
│ │ │ │ ├── grafana-ingress.yaml
│ │ │ │ ├── prometheus-ingress.yaml
│ │ │ │ ├── prometheus-operator.yaml
│ │ │ │ ├── prometheus-relay.yaml
│ │ │ │ └── storage-class.yaml
│ │ │ ├── prometheus-cloud.values.yaml
│ │ │ ├── prometheus-robot.values.yaml
│ │ │ ├── robot/
│ │ │ │ ├── hw-exporter.yaml
│ │ │ │ ├── prometheus-adapter.yaml
│ │ │ │ ├── prometheus-operator.yaml
│ │ │ │ ├── prometheus-relay-client.yaml
│ │ │ │ └── smartctl-exporter.yaml
│ │ │ ├── update_prometheus_adapter.sh
│ │ │ └── values-cloud.yaml
│ │ └── token-vendor/
│ │ ├── BUILD.bazel
│ │ ├── cloud/
│ │ │ ├── dashboard.yaml
│ │ │ ├── ingress.yaml
│ │ │ ├── service-monitor.yaml
│ │ │ ├── service.yaml
│ │ │ ├── token-vendor-policy.yaml
│ │ │ └── token-vendor.yaml
│ │ └── dashboard.json
│ ├── bootstrap/
│ │ ├── cloud/
│ │ │ ├── BUILD.bazel
│ │ │ ├── INSTALL_FROM_BINARY
│ │ │ ├── run-install.sh
│ │ │ └── terraform/
│ │ │ ├── .gitignore
│ │ │ ├── BUILD.bazel
│ │ │ ├── README.md
│ │ │ ├── address.tf
│ │ │ ├── certificate-authority.tf
│ │ │ ├── cluster.tf
│ │ │ ├── dns.tf
│ │ │ ├── endpoints.tf
│ │ │ ├── gcs.tf
│ │ │ ├── input.tf
│ │ │ ├── logging.tf
│ │ │ ├── multi-cluster-ingress.tf
│ │ │ ├── output.tf
│ │ │ ├── project.tf
│ │ │ ├── provider.tf
│ │ │ ├── registry.tf
│ │ │ ├── service-account.tf
│ │ │ ├── versions.tf
│ │ │ ├── workload-identity.tf
│ │ │ └── www.yaml
│ │ └── robot/
│ │ ├── BUILD.bazel
│ │ └── setup_robot.sh
│ ├── go/
│ │ ├── cmd/
│ │ │ ├── app-rollout-controller/
│ │ │ │ ├── BUILD.bazel
│ │ │ │ └── main.go
│ │ │ ├── chart-assignment-controller/
│ │ │ │ ├── BUILD.bazel
│ │ │ │ └── main.go
│ │ │ ├── cr-syncer/
│ │ │ │ ├── BUILD.bazel
│ │ │ │ ├── health.go
│ │ │ │ ├── health_test.go
│ │ │ │ ├── main.go
│ │ │ │ ├── main_test.go
│ │ │ │ ├── syncer.go
│ │ │ │ └── syncer_test.go
│ │ │ ├── cr-syncer-auth-webhook/
│ │ │ │ ├── BUILD.bazel
│ │ │ │ ├── main.go
│ │ │ │ ├── request.go
│ │ │ │ └── request_test.go
│ │ │ ├── gcr-credential-refresher/
│ │ │ │ ├── BUILD.bazel
│ │ │ │ └── main.go
│ │ │ ├── http-relay-client/
│ │ │ │ ├── BUILD.bazel
│ │ │ │ ├── client/
│ │ │ │ │ ├── BUILD.bazel
│ │ │ │ │ ├── client.go
│ │ │ │ │ └── client_test.go
│ │ │ │ └── main.go
│ │ │ ├── http-relay-server/
│ │ │ │ ├── BUILD.bazel
│ │ │ │ ├── README.md
│ │ │ │ ├── main.go
│ │ │ │ └── server/
│ │ │ │ ├── BUILD.bazel
│ │ │ │ ├── broker.go
│ │ │ │ ├── broker_test.go
│ │ │ │ ├── server.go
│ │ │ │ └── server_test.go
│ │ │ ├── hw-exporter/
│ │ │ │ ├── BUILD.bazel
│ │ │ │ ├── main.go
│ │ │ │ └── main_test.go
│ │ │ ├── metadata-server/
│ │ │ │ ├── BUILD.bazel
│ │ │ │ ├── coredns.go
│ │ │ │ ├── coredns_test.go
│ │ │ │ ├── main.go
│ │ │ │ ├── main_test.go
│ │ │ │ ├── metadata.go
│ │ │ │ ├── metadata_test.go
│ │ │ │ └── nftables.go
│ │ │ ├── setup-dev/
│ │ │ │ ├── BUILD.bazel
│ │ │ │ ├── main.go
│ │ │ │ └── setup-dev.md
│ │ │ ├── setup-robot/
│ │ │ │ ├── BUILD.bazel
│ │ │ │ ├── main.go
│ │ │ │ └── main_test.go
│ │ │ ├── synk/
│ │ │ │ ├── BUILD.bazel
│ │ │ │ ├── README.md
│ │ │ │ └── synk.go
│ │ │ └── token-vendor/
│ │ │ ├── BUILD.bazel
│ │ │ ├── README.md
│ │ │ ├── api/
│ │ │ │ ├── BUILD.bazel
│ │ │ │ ├── api.go
│ │ │ │ └── v1/
│ │ │ │ ├── BUILD.bazel
│ │ │ │ ├── testdata/
│ │ │ │ │ ├── README.md
│ │ │ │ │ ├── cloudiot/
│ │ │ │ │ │ ├── describe_device.json
│ │ │ │ │ │ └── describe_device_expired_key.json
│ │ │ │ │ ├── rsa_cert.pem
│ │ │ │ │ └── rsa_private.pem
│ │ │ │ ├── v1.go
│ │ │ │ └── v1_test.go
│ │ │ ├── app/
│ │ │ │ ├── BUILD.bazel
│ │ │ │ ├── tokenvendor.go
│ │ │ │ └── tokenvendor_test.go
│ │ │ ├── main.go
│ │ │ ├── oauth/
│ │ │ │ ├── BUILD.bazel
│ │ │ │ ├── cache.go
│ │ │ │ ├── cache_test.go
│ │ │ │ ├── jwt/
│ │ │ │ │ ├── BUILD.bazel
│ │ │ │ │ ├── jwt.go
│ │ │ │ │ └── jwt_test.go
│ │ │ │ └── verifier.go
│ │ │ ├── repository/
│ │ │ │ ├── BUILD.bazel
│ │ │ │ ├── k8s/
│ │ │ │ │ ├── BUILD.bazel
│ │ │ │ │ ├── k8s.go
│ │ │ │ │ └── k8s_test.go
│ │ │ │ ├── memory/
│ │ │ │ │ ├── BUILD.bazel
│ │ │ │ │ ├── memory.go
│ │ │ │ │ └── memory_test.go
│ │ │ │ └── repository.go
│ │ │ ├── testdata/
│ │ │ │ ├── describe_device_a.json
│ │ │ │ ├── describe_device_b.json
│ │ │ │ ├── describe_device_b_blocked.json
│ │ │ │ └── list_devices.json
│ │ │ └── tokensource/
│ │ │ ├── BUILD.bazel
│ │ │ ├── gcp.go
│ │ │ └── gcp_test.go
│ │ ├── generate.sh
│ │ ├── pkg/
│ │ │ ├── apis/
│ │ │ │ ├── apps/
│ │ │ │ │ └── v1alpha1/
│ │ │ │ │ ├── BUILD.bazel
│ │ │ │ │ ├── doc.go
│ │ │ │ │ ├── register.go
│ │ │ │ │ ├── types.go
│ │ │ │ │ └── zz_generated.deepcopy.go
│ │ │ │ └── registry/
│ │ │ │ └── v1alpha1/
│ │ │ │ ├── BUILD.bazel
│ │ │ │ ├── doc.go
│ │ │ │ ├── register.go
│ │ │ │ ├── types.go
│ │ │ │ └── zz_generated.deepcopy.go
│ │ │ ├── client/
│ │ │ │ ├── informers/
│ │ │ │ │ ├── BUILD.bazel
│ │ │ │ │ ├── apps/
│ │ │ │ │ │ ├── BUILD.bazel
│ │ │ │ │ │ ├── interface.go
│ │ │ │ │ │ └── v1alpha1/
│ │ │ │ │ │ ├── BUILD.bazel
│ │ │ │ │ │ ├── app.go
│ │ │ │ │ │ ├── approllout.go
│ │ │ │ │ │ ├── chartassignment.go
│ │ │ │ │ │ ├── interface.go
│ │ │ │ │ │ └── resourceset.go
│ │ │ │ │ ├── factory.go
│ │ │ │ │ ├── generic.go
│ │ │ │ │ ├── internalinterfaces/
│ │ │ │ │ │ ├── BUILD.bazel
│ │ │ │ │ │ └── factory_interfaces.go
│ │ │ │ │ └── registry/
│ │ │ │ │ ├── BUILD.bazel
│ │ │ │ │ ├── interface.go
│ │ │ │ │ └── v1alpha1/
│ │ │ │ │ ├── BUILD.bazel
│ │ │ │ │ ├── interface.go
│ │ │ │ │ └── robot.go
│ │ │ │ ├── listers/
│ │ │ │ │ ├── apps/
│ │ │ │ │ │ └── v1alpha1/
│ │ │ │ │ │ ├── BUILD.bazel
│ │ │ │ │ │ ├── app.go
│ │ │ │ │ │ ├── approllout.go
│ │ │ │ │ │ ├── chartassignment.go
│ │ │ │ │ │ ├── expansion_generated.go
│ │ │ │ │ │ └── resourceset.go
│ │ │ │ │ └── registry/
│ │ │ │ │ └── v1alpha1/
│ │ │ │ │ ├── BUILD.bazel
│ │ │ │ │ ├── expansion_generated.go
│ │ │ │ │ └── robot.go
│ │ │ │ └── versioned/
│ │ │ │ ├── BUILD.bazel
│ │ │ │ ├── clientset.go
│ │ │ │ ├── doc.go
│ │ │ │ ├── fake/
│ │ │ │ │ ├── BUILD.bazel
│ │ │ │ │ ├── clientset_generated.go
│ │ │ │ │ ├── doc.go
│ │ │ │ │ └── register.go
│ │ │ │ ├── scheme/
│ │ │ │ │ ├── BUILD.bazel
│ │ │ │ │ ├── doc.go
│ │ │ │ │ └── register.go
│ │ │ │ └── typed/
│ │ │ │ ├── apps/
│ │ │ │ │ └── v1alpha1/
│ │ │ │ │ ├── BUILD.bazel
│ │ │ │ │ ├── app.go
│ │ │ │ │ ├── approllout.go
│ │ │ │ │ ├── apps_client.go
│ │ │ │ │ ├── chartassignment.go
│ │ │ │ │ ├── doc.go
│ │ │ │ │ ├── fake/
│ │ │ │ │ │ ├── BUILD.bazel
│ │ │ │ │ │ ├── doc.go
│ │ │ │ │ │ ├── fake_app.go
│ │ │ │ │ │ ├── fake_approllout.go
│ │ │ │ │ │ ├── fake_apps_client.go
│ │ │ │ │ │ ├── fake_chartassignment.go
│ │ │ │ │ │ └── fake_resourceset.go
│ │ │ │ │ ├── generated_expansion.go
│ │ │ │ │ └── resourceset.go
│ │ │ │ └── registry/
│ │ │ │ └── v1alpha1/
│ │ │ │ ├── BUILD.bazel
│ │ │ │ ├── doc.go
│ │ │ │ ├── fake/
│ │ │ │ │ ├── BUILD.bazel
│ │ │ │ │ ├── doc.go
│ │ │ │ │ ├── fake_registry_client.go
│ │ │ │ │ └── fake_robot.go
│ │ │ │ ├── generated_expansion.go
│ │ │ │ ├── registry_client.go
│ │ │ │ └── robot.go
│ │ │ ├── configutil/
│ │ │ │ ├── BUILD.bazel
│ │ │ │ ├── config_reader.go
│ │ │ │ └── config_reader_test.go
│ │ │ ├── controller/
│ │ │ │ ├── approllout/
│ │ │ │ │ ├── BUILD.bazel
│ │ │ │ │ ├── controller.go
│ │ │ │ │ └── controller_test.go
│ │ │ │ └── chartassignment/
│ │ │ │ ├── BUILD.bazel
│ │ │ │ ├── controller.go
│ │ │ │ ├── release.go
│ │ │ │ ├── release_test.go
│ │ │ │ ├── validator.go
│ │ │ │ └── validator_test.go
│ │ │ ├── gcr/
│ │ │ │ ├── BUILD.bazel
│ │ │ │ ├── update_gcr_credential_test.go
│ │ │ │ └── update_gcr_credentials.go
│ │ │ ├── kubetest/
│ │ │ │ ├── BUILD.bazel
│ │ │ │ └── kubetest.go
│ │ │ ├── kubeutils/
│ │ │ │ ├── BUILD.bazel
│ │ │ │ └── kubeutils.go
│ │ │ ├── robotauth/
│ │ │ │ ├── BUILD.bazel
│ │ │ │ ├── robotauth.go
│ │ │ │ └── robotauth_test.go
│ │ │ ├── setup/
│ │ │ │ ├── BUILD.bazel
│ │ │ │ ├── setupcommon.go
│ │ │ │ ├── setupcommon_test.go
│ │ │ │ └── util/
│ │ │ │ ├── BUILD.bazel
│ │ │ │ ├── factory.go
│ │ │ │ └── fake.go
│ │ │ └── synk/
│ │ │ ├── BUILD.bazel
│ │ │ ├── interface.go
│ │ │ ├── sort.go
│ │ │ ├── sort_test.go
│ │ │ ├── synk.go
│ │ │ └── synk_test.go
│ │ └── tests/
│ │ ├── BUILD.bazel
│ │ ├── apps/
│ │ │ ├── BUILD.bazel
│ │ │ ├── apps_test.go
│ │ │ └── run.sh
│ │ ├── k8s_integration_test.go
│ │ ├── k8s_integration_test_auth_helper.go
│ │ ├── relay/
│ │ │ ├── BUILD.bazel
│ │ │ ├── in_process_relay_test.go
│ │ │ └── nok8s_relay_test.go
│ │ ├── relay-bench.sh
│ │ └── relay_test.sh
│ ├── go.mod
│ ├── go.sum
│ ├── gomod.sh
│ └── proto/
│ └── http-relay/
│ ├── BUILD.bazel
│ ├── http_over_rpc.proto
│ └── unused.go
└── third_party/
├── BUILD
├── BUILD.bazel
├── README.md
├── akri/
│ ├── BUILD.bazel
│ ├── akri-0.12.9.tgz
│ ├── akri-configuration-crd.yaml
│ ├── akri-instance-crd.yaml
│ └── update-akri.sh
├── app_crd.BUILD
├── cert-manager/
│ ├── BUILD.bazel
│ └── cert-manager-v1.16.3.tgz
├── cert-manager-google-cas-issuer/
│ ├── BUILD.bazel
│ └── cert-manager-google-cas-issuer-v0.6.2.tgz
├── fluentd_gcp_addon/
│ ├── BUILD.bazel
│ ├── fluentd-gcp-configmap.yaml
│ └── fluentd-gcp-ds.yaml
├── helm2/
│ └── BUILD.bazel
├── helm3/
│ └── BUILD.bazel
├── ingress-nginx.BUILD
├── kube-prometheus-stack/
│ ├── 00-crds.yaml
│ ├── 01-crds.yaml
│ ├── BUILD.bazel
│ ├── kube-prometheus-stack-72.9.1.tgz
│ └── update_crd.sh
├── kubernetes_proto/
│ ├── meta/
│ │ ├── BUILD.bazel
│ │ ├── README.md
│ │ └── generated.proto
│ ├── runtime/
│ │ ├── BUILD.bazel
│ │ └── generated.proto
│ └── schema/
│ ├── BUILD.bazel
│ └── generated.proto
└── terraform.BUILD
SYMBOL INDEX (1320 symbols across 129 files)
FILE: docs/how-to/examples/charge-service/server.py
class Controller (line 9) | class Controller(BaseHTTPRequestHandler):
method sync (line 10) | def sync(self, parent, children):
method do_POST (line 75) | def do_POST(self):
class ChargeService (line 86) | class ChargeService(object):
method __init__ (line 94) | def __init__(self):
method start_charging (line 97) | def start_charging(self):
method get_progress (line 102) | def get_progress(self, request_id):
FILE: docs/how-to/examples/greeter-service/client/client.cc
class GreeterClient (line 35) | class GreeterClient {
method GreeterClient (line 37) | GreeterClient(std::shared_ptr<Channel> channel)
method SayHello (line 42) | std::string SayHello(const std::string& user) {
function main (line 71) | int main(int argc, char** argv) {
FILE: docs/how-to/examples/greeter-service/server/server.cc
class GreeterServiceImpl (line 43) | class GreeterServiceImpl final : public Greeter::Service {
method Status (line 44) | Status SayHello(ServerContext* context, const HelloRequest* request,
function RunServer (line 54) | void RunServer() {
function main (line 78) | int main(int argc, char** argv) {
FILE: docs/how-to/examples/hello-service/server/server.py
class MyRequestHandler (line 6) | class MyRequestHandler(server.BaseHTTPRequestHandler):
method do_GET (line 7) | def do_GET(self):
function main (line 15) | def main():
FILE: src/go/cmd/app-rollout-controller/main.go
function main (line 53) | func main() {
function runController (line 78) | func runController(ctx context.Context, cfg *rest.Config, params map[str...
FILE: src/go/cmd/chart-assignment-controller/main.go
function main (line 56) | func main() {
function runController (line 104) | func runController(ctx context.Context, cfg *rest.Config, cluster string...
FILE: src/go/cmd/cr-syncer-auth-webhook/main.go
constant verifyJWTEndpoint (line 53) | verifyJWTEndpoint = "/apis/core.token-vendor/v1/jwt.verify"
constant legacyTokenPrefix (line 55) | legacyTokenPrefix = "ya29."
type handlers (line 58) | type handlers struct
method health (line 68) | func (h *handlers) health(w http.ResponseWriter, r *http.Request) {
method verifyJWT (line 74) | func (h *handlers) verifyJWT(encodedJWT string) error {
method resourceIsFiltered (line 103) | func (h *handlers) resourceIsFiltered(groupKind string) bool {
method validateRequest (line 111) | func (h *handlers) validateRequest(r *http.Request, robotName string) ...
method auth (line 146) | func (h *handlers) auth(w http.ResponseWriter, r *http.Request) {
function newHandlers (line 62) | func newHandlers() handlers {
function main (line 186) | func main() {
FILE: src/go/cmd/cr-syncer-auth-webhook/request.go
constant robotNameSelectorPrefix (line 21) | robotNameSelectorPrefix = "cloudrobotics.com/robot-name="
type incomingRequest (line 24) | type incomingRequest struct
function parseURL (line 37) | func parseURL(urlString string) (*incomingRequest, error) {
FILE: src/go/cmd/cr-syncer-auth-webhook/request_test.go
function TestParseURL (line 9) | func TestParseURL(t *testing.T) {
function TestParseURLErrors (line 76) | func TestParseURLErrors(t *testing.T) {
FILE: src/go/cmd/cr-syncer/health.go
type handler (line 27) | type handler struct
method ServeHTTP (line 36) | func (h *handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
function newHealthHandler (line 32) | func newHealthHandler(ctx context.Context, client dynamic.Interface) htt...
FILE: src/go/cmd/cr-syncer/health_test.go
function TestHealthy (line 16) | func TestHealthy(t *testing.T) {
function TestHealthyForBadRequest (line 35) | func TestHealthyForBadRequest(t *testing.T) {
function TestUnhealthy (line 58) | func TestUnhealthy(t *testing.T) {
FILE: src/go/cmd/cr-syncer/main.go
constant resyncPeriod (line 82) | resyncPeriod = 5 * time.Minute
function init (line 101) | func init() {
type PrefixingRoundtripper (line 140) | type PrefixingRoundtripper struct
method RoundTrip (line 145) | func (pr *PrefixingRoundtripper) RoundTrip(r *http.Request) (*http.Res...
type ctxRoundTripper (line 157) | type ctxRoundTripper struct
method RoundTrip (line 162) | func (r *ctxRoundTripper) RoundTrip(req *http.Request) (*http.Response...
function restConfigForRemote (line 167) | func restConfigForRemote(ctx context.Context) (*rest.Config, error) {
type CrdChange (line 207) | type CrdChange struct
function streamCrds (line 212) | func streamCrds(done <-chan struct{}, clientset crdclientset.Interface, ...
function main (line 238) | func main() {
function mustNewTagKey (line 336) | func mustNewTagKey(s string) tag.Key {
FILE: src/go/cmd/cr-syncer/main_test.go
function TestStreamCrdsSeesPreexistingObject (line 31) | func TestStreamCrdsSeesPreexistingObject(t *testing.T) {
function TestStreamCrdsSeesAdditionAndDeletion (line 64) | func TestStreamCrdsSeesAdditionAndDeletion(t *testing.T) {
function TestStreamCrdsSeesUpdate (line 102) | func TestStreamCrdsSeesUpdate(t *testing.T) {
FILE: src/go/cmd/cr-syncer/syncer.go
constant annotationStatusSubtree (line 45) | annotationStatusSubtree = "cr-syncer.cloudrobotics.com/status-subtree"
constant annotationFilterByRobotName (line 46) | annotationFilterByRobotName = "cr-syncer.cloudrobotics.com/filter-by-rob...
constant annotationSpecSource (line 47) | annotationSpecSource = "cr-syncer.cloudrobotics.com/spec-source"
constant labelRobotName (line 50) | labelRobotName = "cloudrobotics.com/robot-name"
constant annotationResourceVersion (line 56) | annotationResourceVersion = "cr-syncer.cloudrobotics.com/remote-resource...
constant cloudClusterName (line 58) | cloudClusterName = "cloud"
function init (line 80) | func init() {
function removeFinalizer (line 104) | func removeFinalizer(ctx context.Context, client dynamic.ResourceInterfa...
type crSyncer (line 131) | type crSyncer struct
method newInformer (line 237) | func (s *crSyncer) newInformer(client dynamic.ResourceInterface) cache...
method startInformers (line 257) | func (s *crSyncer) startInformers() error {
method stopInformers (line 278) | func (s *crSyncer) stopInformers() {
method restartInformers (line 285) | func (s *crSyncer) restartInformers() error {
method setupInformerHandlers (line 292) | func (s *crSyncer) setupInformerHandlers(
method processNextWorkItem (line 322) | func (s *crSyncer) processNextWorkItem(
method run (line 373) | func (s *crSyncer) run() {
method stop (line 405) | func (s *crSyncer) stop() {
method syncDownstream (line 413) | func (s *crSyncer) syncDownstream(key string) error {
method syncUpstream (line 521) | func (s *crSyncer) syncUpstream(key string) error {
function getStorageVersionIndex (line 156) | func getStorageVersionIndex(crd crdtypes.CustomResourceDefinition) (int,...
function newCRSyncer (line 165) | func newCRSyncer(
function isNotFoundError (line 617) | func isNotFoundError(err error) bool {
type apiError (line 622) | type apiError struct
method Error (line 627) | func (e apiError) Error() string {
function newAPIErrorf (line 631) | func newAPIErrorf(o *unstructured.Unstructured, format string, args ...i...
function keyFunc (line 637) | func keyFunc(obj interface{}) (string, bool) {
function setAnnotation (line 646) | func setAnnotation(o *unstructured.Unstructured, key, value string) {
function deleteAnnotation (line 655) | func deleteAnnotation(o *unstructured.Unstructured, key string) {
function copyStatus (line 668) | func copyStatus(dst, src *unstructured.Unstructured) {
FILE: src/go/cmd/cr-syncer/syncer_test.go
function filterReadActions (line 38) | func filterReadActions(actions []k8stest.Action) (ret []k8stest.Action) {
type fixture (line 48) | type fixture struct
method newCRSyncer (line 66) | func (f *fixture) newCRSyncer(crd crdtypes.CustomResourceDefinition, r...
method addLocalObjects (line 107) | func (f *fixture) addLocalObjects(objs ...runtime.Object) {
method addRemoteObjects (line 111) | func (f *fixture) addRemoteObjects(objs ...runtime.Object) {
method expectLocalActions (line 115) | func (f *fixture) expectLocalActions(as ...k8stest.Action) {
method expectRemoteActions (line 119) | func (f *fixture) expectRemoteActions(as ...k8stest.Action) {
method verifyWriteActions (line 123) | func (f *fixture) verifyWriteActions() {
function newFixture (line 62) | func newFixture(t *testing.T) *fixture {
function testCRD (line 139) | func testCRD(scope crdtypes.ResourceScope) crdtypes.CustomResourceDefini...
function newTestCR (line 166) | func newTestCR(name string, spec, status interface{}) *unstructured.Unst...
function newClusterScopedTestCR (line 181) | func newClusterScopedTestCR(name string, spec, status interface{}) *unst...
function TestSyncUpstream_createSpec (line 194) | func TestSyncUpstream_createSpec(t *testing.T) {
function TestSyncClusterScopedCRUpstream_createSpec (line 218) | func TestSyncClusterScopedCRUpstream_createSpec(t *testing.T) {
function TestSyncUpstream_updateSpec (line 242) | func TestSyncUpstream_updateSpec(t *testing.T) {
function TestSyncUpstream_propagateDelete (line 269) | func TestSyncUpstream_propagateDelete(t *testing.T) {
function TestSyncDownstream_deleteOrphan (line 297) | func TestSyncDownstream_deleteOrphan(t *testing.T) {
function TestSyncDownstream_statusFull (line 321) | func TestSyncDownstream_statusFull(t *testing.T) {
function TestSyncDownstream_statusWithObservedGeneration (line 351) | func TestSyncDownstream_statusWithObservedGeneration(t *testing.T) {
function TestSyncDownstream_statusSubtree (line 390) | func TestSyncDownstream_statusSubtree(t *testing.T) {
function TestSyncDownstream_downstreamNotFound (line 430) | func TestSyncDownstream_downstreamNotFound(t *testing.T) {
function TestCRSyncer_populateWorkqueue (line 476) | func TestCRSyncer_populateWorkqueue(t *testing.T) {
function TestCRSyncer_populateWorkqueueWithFilter (line 517) | func TestCRSyncer_populateWorkqueueWithFilter(t *testing.T) {
function channelFromQueue (line 555) | func channelFromQueue(t *testing.T, queue workqueue.Interface, inf cache...
FILE: src/go/cmd/gcr-credential-refresher/main.go
constant updateInterval (line 34) | updateInterval = 10 * time.Minute
function updateCredentials (line 37) | func updateCredentials(ctx context.Context) error {
function main (line 64) | func main() {
FILE: src/go/cmd/http-relay-client/client/client.go
type ClientConfig (line 66) | type ClientConfig struct
type RelayServerError (line 97) | type RelayServerError struct
method Error (line 105) | func (e *RelayServerError) Error() string {
function NewRelayServerError (line 101) | func NewRelayServerError(msg string) error {
function DefaultClientConfig (line 109) | func DefaultClientConfig() ClientConfig {
type Client (line 148) | type Client struct
method Start (line 158) | func (c *Client) Start() {
method getRequest (line 268) | func (c *Client) getRequest(remote *http.Client, relayURL string) (*pb...
method createBackendRequest (line 317) | func (c *Client) createBackendRequest(breq *pb.HttpRequest) (*http.Req...
method postResponse (line 402) | func (c *Client) postResponse(remote *http.Client, br *pb.HttpResponse...
method streamBytes (line 437) | func (c *Client) streamBytes(id string, in io.ReadCloser, out chan<- [...
method buildResponses (line 470) | func (c *Client) buildResponses(in <-chan []byte, resp *pb.HttpRespons...
method postErrorResponse (line 517) | func (c *Client) postErrorResponse(remote *http.Client, id string, mes...
method streamToBackend (line 539) | func (c *Client) streamToBackend(remote *http.Client, id string, backe...
method handleRequest (line 594) | func (c *Client) handleRequest(remote *http.Client, local *http.Client...
method localProxy (line 720) | func (c *Client) localProxy(remote, local *http.Client) error {
method localProxyWorker (line 763) | func (c *Client) localProxyWorker(remote, local *http.Client) {
method buildRelayURL (line 774) | func (c *Client) buildRelayURL() string {
function NewClient (line 152) | func NewClient(config ClientConfig) *Client {
function addServiceName (line 263) | func addServiceName(span *trace.Span) {
function marshalHeader (line 301) | func marshalHeader(h *http.Header) []*pb.HttpHeader {
function extractRequestHeader (line 311) | func extractRequestHeader(breq *pb.HttpRequest, header *http.Header) {
function makeBackendRequest (line 362) | func makeBackendRequest(ctx context.Context, local *http.Client, req *ht...
FILE: src/go/cmd/http-relay-client/client/client_test.go
function assertMocksDoneWithin (line 30) | func assertMocksDoneWithin(t *testing.T, d time.Duration) {
function TestAssertMocksDoneWithin_SucceedsWhenMocksAreDone (line 42) | func TestAssertMocksDoneWithin_SucceedsWhenMocksAreDone(t *testing.T) {
function TestAssertMocksDoneWithin_FailsWhenMocksNotDone (line 46) | func TestAssertMocksDoneWithin_FailsWhenMocksNotDone(t *testing.T) {
function TestLocalProxy (line 56) | func TestLocalProxy(t *testing.T) {
function TestBackendError (line 114) | func TestBackendError(t *testing.T) {
function TestServerTimeout (line 183) | func TestServerTimeout(t *testing.T) {
function TestBuildResponsesTimesOut (line 214) | func TestBuildResponsesTimesOut(t *testing.T) {
FILE: src/go/cmd/http-relay-client/main.go
function init (line 45) | func init() {
function main (line 97) | func main() {
FILE: src/go/cmd/http-relay-server/main.go
function main (line 48) | func main() {
FILE: src/go/cmd/http-relay-server/server/broker.go
function init (line 69) | func init() {
type pendingResponse (line 77) | type pendingResponse struct
type RelayClientUnavailableError (line 100) | type RelayClientUnavailableError struct
method Error (line 104) | func (e *RelayClientUnavailableError) Error() string {
type broker (line 114) | type broker struct
method Healthy (line 129) | func (r *broker) Healthy() error {
method RelayRequest (line 137) | func (r *broker) RelayRequest(server string, request *pb.HttpRequest) ...
method StopRelayRequest (line 182) | func (r *broker) StopRelayRequest(requestId string) {
method GetRequest (line 190) | func (r *broker) GetRequest(ctx context.Context, server, path string) ...
method GetRequestStream (line 217) | func (r *broker) GetRequestStream(id string) ([]byte, bool) {
method PutRequestStream (line 237) | func (r *broker) PutRequestStream(id string, data []byte) bool {
method SendResponse (line 260) | func (r *broker) SendResponse(resp *pb.HttpResponse) error {
method ReapInactiveRequests (line 317) | func (r *broker) ReapInactiveRequests(threshold time.Time) {
function newBroker (line 120) | func newBroker() *broker {
FILE: src/go/cmd/http-relay-server/server/broker_test.go
constant idOne (line 32) | idOne = "idOne"
constant idTwo (line 33) | idTwo = "idTwo"
constant idThree (line 34) | idThree = "idThree"
constant unknownID (line 35) | unknownID = "unknownID"
type brokerConn (line 43) | type brokerConn struct
method runSender (line 53) | func (bt *brokerConn) runSender(t *testing.T, b *broker, s string, m s...
method bakeRequest (line 70) | func (bt *brokerConn) bakeRequest(b *broker, s string) {
method runReceiver (line 80) | func (bt *brokerConn) runReceiver(t *testing.T, b *broker, s string, w...
method runSenderStream (line 94) | func (bt *brokerConn) runSenderStream(t *testing.T, b *broker, s strin...
method runReceiverStream (line 117) | func (bt *brokerConn) runReceiverStream(t *testing.T, b *broker, s str...
function newBrokerConn (line 47) | func newBrokerConn() brokerConn {
function TestNormalCase (line 137) | func TestNormalCase(t *testing.T) {
function TestResponseStream (line 155) | func TestResponseStream(t *testing.T) {
function TestMissingId (line 167) | func TestMissingId(t *testing.T) {
function TestDuplicateId (line 175) | func TestDuplicateId(t *testing.T) {
function TestRequestStream (line 190) | func TestRequestStream(t *testing.T) {
function TestRequestStreamUnknownID (line 220) | func TestRequestStreamUnknownID(t *testing.T) {
function TestTimeout (line 230) | func TestTimeout(t *testing.T) {
function TestReapWhileSendingResponse (line 259) | func TestReapWhileSendingResponse(t *testing.T) {
function TestReapWhileSendingRequest (line 307) | func TestReapWhileSendingRequest(t *testing.T) {
FILE: src/go/cmd/http-relay-server/server/server.go
constant clientPrefix (line 48) | clientPrefix = "/client/"
constant cleanShutdownTimeout (line 52) | cleanShutdownTimeout = 20 * time.Second
constant debugLogs (line 54) | debugLogs = false
constant DefaultPort (line 57) | DefaultPort = 80
constant DefaultBlockSize (line 59) | DefaultBlockSize = 10 * 1024
constant DefaultInactiveRequestTimeout (line 61) | DefaultInactiveRequestTimeout = 60 * time.Second
type Config (line 64) | type Config struct
type Server (line 73) | type Server struct
method responseFilter (line 164) | func (s *Server) responseFilter(backendCtx backendContext, in <-chan *...
method health (line 223) | func (s *Server) health(w http.ResponseWriter, r *http.Request) {
method bidirectionalStream (line 241) | func (s *Server) bidirectionalStream(backendCtx backendContext, w http...
method readRequestBody (line 299) | func (s *Server) readRequestBody(ctx context.Context, r *http.Request)...
method createBackendRequest (line 306) | func (s *Server) createBackendRequest(backendCtx backendContext, r *ht...
method relayRequest (line 327) | func (s *Server) relayRequest(ctx context.Context, backendCtx backendC...
method waitForFirstResponseAndHandleSwitching (line 339) | func (s *Server) waitForFirstResponseAndHandleSwitching(ctx context.Co...
method userClientRequest (line 366) | func (s *Server) userClientRequest(w http.ResponseWriter, r *http.Requ...
method serverRequest (line 463) | func (s *Server) serverRequest(w http.ResponseWriter, r *http.Request) {
method serverRequestStream (line 492) | func (s *Server) serverRequestStream(w http.ResponseWriter, r *http.Re...
method serverResponse (line 515) | func (s *Server) serverResponse(w http.ResponseWriter, r *http.Request) {
method Start (line 542) | func (s *Server) Start() {
function NewServer (line 78) | func NewServer(conf Config) *Server {
function createId (line 100) | func createId() string {
function marshalHeader (line 107) | func marshalHeader(h *http.Header) []*pb.HttpHeader {
function unmarshalHeader (line 117) | func unmarshalHeader(w http.ResponseWriter, protoHeader []*pb.HttpHeader) {
function addServiceName (line 123) | func addServiceName(span *trace.Span) {
function extractBackendNameAndPath (line 128) | func extractBackendNameAndPath(r *http.Request) (backendName string, pat...
type responseChunk (line 155) | type responseChunk struct
type backendContext (line 205) | type backendContext struct
function newBackendContext (line 211) | func newBackendContext(r *http.Request) (*backendContext, error) {
FILE: src/go/cmd/http-relay-server/server/server_test.go
function checkResponse (line 34) | func checkResponse(t *testing.T, resp *http.Response, wantStatus int, wa...
function TestClientHandler (line 48) | func TestClientHandler(t *testing.T) {
function TestClientHandlerWithChunkedResponse (line 116) | func TestClientHandlerWithChunkedResponse(t *testing.T) {
function TestClientBadRequest (line 189) | func TestClientBadRequest(t *testing.T) {
function nonRepeatingByteArray (line 236) | func nonRepeatingByteArray(n int) []byte {
function TestRequestStreamHandler (line 246) | func TestRequestStreamHandler(t *testing.T) {
function TestServerRequestResponseHandler (line 304) | func TestServerRequestResponseHandler(t *testing.T) {
function TestServerResponseHandlerWithInvalidRequestID (line 376) | func TestServerResponseHandlerWithInvalidRequestID(t *testing.T) {
function TestRequestToUnknownBackendResponse503 (line 404) | func TestRequestToUnknownBackendResponse503(t *testing.T) {
FILE: src/go/cmd/hw-exporter/main.go
type pciCollector (line 30) | type pciCollector struct
method Describe (line 46) | func (c *pciCollector) Describe(ch chan<- *prometheus.Desc) {
method Collect (line 60) | func (c *pciCollector) Collect(ch chan<- prometheus.Metric) {
function newPciCollector (line 34) | func newPciCollector() *pciCollector {
function getNameOrID (line 51) | func getNameOrID(name, id string) string {
function main (line 81) | func main() {
FILE: src/go/cmd/hw-exporter/main_test.go
function TestPciCollector_Collect (line 42) | func TestPciCollector_Collect(t *testing.T) {
FILE: src/go/cmd/metadata-server/coredns.go
constant configMapName (line 29) | configMapName = "coredns"
constant configMapNamespace (line 30) | configMapNamespace = "kube-system"
constant corefileName (line 31) | corefileName = "Corefile"
constant zoneStart (line 32) | zoneStart = ".:53 {\n"
constant zoneStartPatched (line 33) | zoneStartPatched = `.:53 {
constant hostsStart (line 39) | hostsStart = "hosts {\n"
constant hostsStartPatched (line 40) | hostsStartPatched = `hosts hosts metadata.google.internal host.minikube....
function getCorefile (line 50) | func getCorefile(ctx context.Context, k8s kubernetes.Interface) (*v1.Con...
function writeCorefile (line 64) | func writeCorefile(ctx context.Context, k8s kubernetes.Interface, cm *v1...
function PatchCorefile (line 71) | func PatchCorefile(ctx context.Context, k8s kubernetes.Interface) error {
function RevertCorefile (line 93) | func RevertCorefile(ctx context.Context, k8s kubernetes.Interface) error {
FILE: src/go/cmd/metadata-server/coredns_test.go
constant defaultCorefileBeforeMinikube121 (line 30) | defaultCorefileBeforeMinikube121 = `.:53 {
constant modifiedCorefileBeforeMinikube121 (line 34) | modifiedCorefileBeforeMinikube121 = `.:53 {
constant defaultCorefileAfterMinikube121 (line 42) | defaultCorefileAfterMinikube121 = `.:53 {
constant modifiedCorefileAfterMinikube121 (line 50) | modifiedCorefileAfterMinikube121 = `.:53 {
constant defaultCorefileUnexpected (line 59) | defaultCorefileUnexpected = `.:53 {
constant differentIp (line 67) | differentIp = "1.2.3.456"
function createCorefile (line 70) | func createCorefile(t *testing.T, k8s kubernetes.Interface, corefileData...
function readCorefile (line 86) | func readCorefile(t *testing.T, k8s kubernetes.Interface) string {
function TestPatchCorefile (line 100) | func TestPatchCorefile(t *testing.T) {
function TestPatchCorefileUnexpected (line 163) | func TestPatchCorefileUnexpected(t *testing.T) {
FILE: src/go/cmd/metadata-server/main.go
function detectChangesToFile (line 54) | func detectChangesToFile(filename string) <-chan struct{} {
function main (line 88) | func main() {
FILE: src/go/cmd/metadata-server/main_test.go
constant writeTimeout (line 24) | writeTimeout = 100 * time.Millisecond
function TestDetectsDeletionOfFile (line 27) | func TestDetectsDeletionOfFile(t *testing.T) {
function TestNoChangeDetectedWhenFileUnchanged (line 47) | func TestNoChangeDetectedWhenFileUnchanged(t *testing.T) {
FILE: src/go/cmd/metadata-server/metadata.go
constant getPodByIPRetries (line 43) | getPodByIPRetries = 10
constant getPodByIPWait (line 45) | getPodByIPWait = 500 * time.Millisecond
type rateLimitTokenSource (line 51) | type rateLimitTokenSource struct
method Token (line 70) | func (s *rateLimitTokenSource) Token() (*oauth2.Token, error) {
method resetBackoff (line 85) | func (s *rateLimitTokenSource) resetBackoff() {
method updateBackoff (line 90) | func (s *rateLimitTokenSource) updateBackoff(err error) {
function newRateLimitTokenSource (line 60) | func newRateLimitTokenSource(ts oauth2.TokenSource) *rateLimitTokenSource {
type ConstHandler (line 99) | type ConstHandler struct
method ServeHTTP (line 103) | func (ch ConstHandler) ServeHTTP(w http.ResponseWriter, r *http.Reques...
type jwtSource (line 110) | type jwtSource interface
type IdentityHandler (line 115) | type IdentityHandler struct
method ServeHTTP (line 153) | func (h *IdentityHandler) ServeHTTP(w http.ResponseWriter, r *http.Req...
function NewIdentityHandler (line 120) | func NewIdentityHandler(ctx context.Context) (*IdentityHandler, error) {
function fromAcceptedIP (line 137) | func fromAcceptedIP(w http.ResponseWriter, r *http.Request, allowedSourc...
type TokenHandler (line 175) | type TokenHandler struct
method updateRobotAuth (line 227) | func (th *TokenHandler) updateRobotAuth() error {
method updateRobotTokenSource (line 236) | func (th *TokenHandler) updateRobotTokenSource(ctx context.Context) {
method NewMetadataHandler (line 247) | func (th *TokenHandler) NewMetadataHandler(ctx context.Context) *Metad...
method ServeHTTP (line 281) | func (th *TokenHandler) ServeHTTP(w http.ResponseWriter, r *http.Reque...
method getPodNameByIP (line 332) | func (th *TokenHandler) getPodNameByIP(ctx context.Context, ip string)...
type auth (line 184) | type auth interface
type rAuth (line 190) | type rAuth struct
method projectID (line 194) | func (a rAuth) projectID() string {
method robotName (line 198) | func (a rAuth) robotName() string {
type TokenResponse (line 202) | type TokenResponse struct
function NewTokenHandler (line 208) | func NewTokenHandler(ctx context.Context, k8s *kubernetes.Clientset, saN...
type ServiceAccountHandler (line 403) | type ServiceAccountHandler struct
method ServeHTTP (line 412) | func (sh ServiceAccountHandler) ServeHTTP(w http.ResponseWriter, r *ht...
type ServiceAccountResponse (line 406) | type ServiceAccountResponse struct
type MetadataHandler (line 434) | type MetadataHandler struct
method ServeHTTP (line 443) | func (mh MetadataHandler) ServeHTTP(w http.ResponseWriter, r *http.Req...
function getProjectNumber (line 479) | func getProjectNumber(client *http.Client, projectId string) (int64, err...
FILE: src/go/cmd/metadata-server/metadata_test.go
function bodyOrDie (line 33) | func bodyOrDie(r *http.Response) string {
function TestConstHandler (line 42) | func TestConstHandler(t *testing.T) {
type fakeJWTSource (line 56) | type fakeJWTSource struct
method CreateJWT (line 61) | func (s *fakeJWTSource) CreateJWT(_ context.Context, d time.Duration) ...
function TestIdentityHandlerServeHTTP (line 66) | func TestIdentityHandlerServeHTTP(t *testing.T) {
function TestTokenHandlerServesToken (line 100) | func TestTokenHandlerServesToken(t *testing.T) {
type fakeRobotAuth (line 123) | type fakeRobotAuth struct
method CreateRobotTokenSource (line 129) | func (a *fakeRobotAuth) CreateRobotTokenSource(context.Context, ...str...
method projectID (line 133) | func (a *fakeRobotAuth) projectID() string {
method robotName (line 137) | func (a *fakeRobotAuth) robotName() string {
function TestTokenHandlerServesLastingToken (line 141) | func TestTokenHandlerServesLastingToken(t *testing.T) {
function TestTokenHandlerDeniesWrongAddress (line 167) | func TestTokenHandlerDeniesWrongAddress(t *testing.T) {
function TestServiceAccountHandlerReturnsMinimalJSON (line 182) | func TestServiceAccountHandlerReturnsMinimalJSON(t *testing.T) {
function TestMetadataHandlerReturnsZone (line 197) | func TestMetadataHandlerReturnsZone(t *testing.T) {
type fakeTokenSource (line 221) | type fakeTokenSource struct
method Token (line 226) | func (s *fakeTokenSource) Token() (*oauth2.Token, error) {
function TestRateLimitTokenSource (line 234) | func TestRateLimitTokenSource(t *testing.T) {
FILE: src/go/cmd/metadata-server/nftables.go
function addNATRule (line 30) | func addNATRule(listenIP string, listenPort int) error {
function removeNATRule (line 145) | func removeNATRule() {
FILE: src/go/cmd/setup-dev/main.go
constant robotPrefix (line 46) | robotPrefix = "dev-"
function parseFlags (line 53) | func parseFlags() {
function main (line 62) | func main() {
function createKubeRelayEntry (line 117) | func createKubeRelayEntry(projectID string, domain string, robotName str...
function setupDevCredentials (line 151) | func setupDevCredentials(client *http.Client, domain string, robotName s...
function makeIdentifier (line 177) | func makeIdentifier(base string) string {
function containerExists (line 182) | func containerExists(container string) (bool, error) {
function stopContainerIfNeeded (line 195) | func stopContainerIfNeeded(container string) error {
FILE: src/go/cmd/setup-robot/main.go
constant filesDir (line 75) | filesDir = "/setup-robot-files"
constant helmPath (line 76) | helmPath = filesDir + "/helm"
constant synkPath (line 77) | synkPath = filesDir + "/synk"
constant numDNSRetries (line 78) | numDNSRetries = 6
constant numServiceRetries (line 79) | numServiceRetries = 6
constant commaSentinel (line 81) | commaSentinel = "_COMMA_SENTINEL_"
constant baseNamespace (line 82) | baseNamespace = "default"
function parseFlags (line 85) | func parseFlags() {
function parseKeyValues (line 127) | func parseKeyValues(s string) (map[string]string, error) {
function checkRobotName (line 151) | func checkRobotName(ctx context.Context, client dynamic.Interface) error {
function main (line 172) | func main() {
function helmValuesStringFromMap (line 322) | func helmValuesStringFromMap(varMap map[string]string) string {
function installChartOrDie (line 331) | func installChartOrDie(ctx context.Context, cs *kubernetes.Clientset, do...
function createOrUpdateRobot (line 398) | func createOrUpdateRobot(ctx context.Context, k8sDynamicClient dynamic.I...
FILE: src/go/cmd/setup-robot/main_test.go
function TestParseKeyValues_ReturnsEmptyMapOnEmptyInput (line 34) | func TestParseKeyValues_ReturnsEmptyMapOnEmptyInput(t *testing.T) {
function TestParseKeyValues_HandlesSingleEntry (line 41) | func TestParseKeyValues_HandlesSingleEntry(t *testing.T) {
function TestParseKeyValues_HandlesMultipleEntries (line 55) | func TestParseKeyValues_HandlesMultipleEntries(t *testing.T) {
function TestParseKeyValues_HandlesEscapedCommas (line 76) | func TestParseKeyValues_HandlesEscapedCommas(t *testing.T) {
function TestParseKeyValues_HandlesSpaces (line 97) | func TestParseKeyValues_HandlesSpaces(t *testing.T) {
function TestCheckRobotName_SucceedsWhenCRDNotFound (line 111) | func TestCheckRobotName_SucceedsWhenCRDNotFound(t *testing.T) {
function TestCheckRobotName (line 137) | func TestCheckRobotName(t *testing.T) {
function TestCreateOrUpdateRobot_Succeeds (line 200) | func TestCreateOrUpdateRobot_Succeeds(t *testing.T) {
FILE: src/go/cmd/synk/synk.go
constant retryBackoff (line 39) | retryBackoff = 5 * time.Second
function main (line 70) | func main() {
function newSynk (line 90) | func newSynk() (*synk.Synk, error) {
function runInit (line 114) | func runInit(cmd *cobra.Command, args []string) {
function runDelete (line 127) | func runDelete(cmd *cobra.Command, args []string) {
function runApply (line 144) | func runApply(cmd *cobra.Command, args []string) {
function apply (line 155) | func apply(name string) error {
function logAction (line 210) | func logAction(r *unstructured.Unstructured, action apps.ResourceAction,...
FILE: src/go/cmd/token-vendor/api/api.go
constant httpTimeoutRead (line 27) | httpTimeoutRead = 10 * time.Second
constant httpTimeoutWrite (line 28) | httpTimeoutWrite = 10 * time.Second
constant httpTimeoutHandler (line 29) | httpTimeoutHandler = 10 * time.Second
type constHandler (line 32) | type constHandler
method ServeHTTP (line 34) | func (ch constHandler) ServeHTTP(w http.ResponseWriter, r *http.Reques...
function Register (line 42) | func Register() error {
function SetupAndServe (line 49) | func SetupAndServe(addr string) error {
function LoggingMiddleware (line 61) | func LoggingMiddleware(handler http.Handler) http.Handler {
function ErrResponse (line 75) | func ErrResponse(ctx context.Context, w http.ResponseWriter, statusCode ...
FILE: src/go/cmd/token-vendor/api/v1/v1.go
constant paramDeviceID (line 39) | paramDeviceID = "device-id"
constant contentType (line 40) | contentType = "content-type"
constant pemFile (line 41) | pemFile = "application/x-pem-file"
type HandlerContext (line 44) | type HandlerContext struct
method publicKeyConfigureHandler (line 75) | func (h *HandlerContext) publicKeyConfigureHandler(w http.ResponseWrit...
method publicKeyReadHandler (line 119) | func (h *HandlerContext) publicKeyReadHandler(w http.ResponseWriter, r...
method publicKeyPublishHandler (line 157) | func (h *HandlerContext) publicKeyPublishHandler(w http.ResponseWriter...
method tokenOAuth2Handler (line 262) | func (h *HandlerContext) tokenOAuth2Handler(w http.ResponseWriter, r *...
method verifyJWTHandler (line 320) | func (h *HandlerContext) verifyJWTHandler(w http.ResponseWriter, r *ht...
method verifyTokenHandler (line 364) | func (h *HandlerContext) verifyTokenHandler(w http.ResponseWriter, r *...
function NewHandlerContext (line 48) | func NewHandlerContext(tv *app.TokenVendor) *HandlerContext {
function getQueryParam (line 55) | func getQueryParam(u *url.URL, param string) (string, error) {
function isValidPublicKey (line 194) | func isValidPublicKey(pk []byte) (bool, error) {
function testForRobotACL (line 385) | func testForRobotACL(u *url.URL) bool {
function tokenFromRequest (line 400) | func tokenFromRequest(u *url.URL, h *http.Header) (string, error) {
constant tokenRegex (line 433) | tokenRegex = `^ya29\.[a-zA-Z0-9\.\-_]+$`
function isValidToken (line 440) | func isValidToken(token string) (bool, error) {
constant jwtRegex (line 454) | jwtRegex = `^[a-zA-Z0-9\.\-_]+\.[a-zA-Z0-9\.\-_]+\.[a-zA-Z0-9\.\-_]+$`
function isValidJWT (line 461) | func isValidJWT(jwt string) (bool, error) {
function Register (line 474) | func Register(tv *app.TokenVendor, prefix string) error {
FILE: src/go/cmd/token-vendor/api/v1/v1_test.go
constant testPubKey (line 28) | testPubKey = "testdata/rsa_cert.pem"
constant jwtBodyPrefix (line 29) | jwtBodyPrefix = "grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&...
constant saName (line 30) | saName = "robot-service@testproject.iam.gserviceaccount.com"
constant jwtCorrect (line 32) | jwtCorrect = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJ0ZXN0YXVk...
constant jwtWrongSig (line 33) | jwtWrongSig = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJ0ZXN0YXVk...
constant jwtWrongAud (line 34) | jwtWrongAud = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJhYmMiLCJp...
constant jwtExpired (line 35) | jwtExpired = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJ0ZXN0YXVk...
type RoundTripFunc (line 38) | type RoundTripFunc
method RoundTrip (line 40) | func (f RoundTripFunc) RoundTrip(req *http.Request) (*http.Response, e...
function NewTestHTTPClient (line 44) | func NewTestHTTPClient(fn RoundTripFunc) *http.Client {
type publicKeyConfigureHandlerK8sTest (line 48) | type publicKeyConfigureHandlerK8sTest struct
function TestPublicKeyConfigureHandlerWithK8s (line 57) | func TestPublicKeyConfigureHandlerWithK8s(t *testing.T) {
function runPublicKeyConfigureHandlerWithK8sCase (line 106) | func runPublicKeyConfigureHandlerWithK8sCase(t *testing.T, test *publicK...
type publicKeyReadHandlerK8sTest (line 138) | type publicKeyReadHandlerK8sTest struct
function TestPublicKeyReadHandlerWithK8s (line 146) | func TestPublicKeyReadHandlerWithK8s(t *testing.T) {
function populateK8sEnv (line 203) | func populateK8sEnv(env kubernetes.Interface, ns string, maps []*corev1....
function runPublicKeyReadHandlerWithK8sCase (line 212) | func runPublicKeyReadHandlerWithK8sCase(t *testing.T, test *publicKeyRea...
function mustRespBodyFromFile (line 252) | func mustRespBodyFromFile(t *testing.T, file string) io.ReadCloser {
function mustNewRequest (line 260) | func mustNewRequest(t *testing.T, method, url string, body io.Reader) *h...
function mustFileOpen (line 268) | func mustFileOpen(t *testing.T, name string) io.Reader {
function mustFileToString (line 276) | func mustFileToString(t *testing.T, name string) string {
type publicKeyPublishHandlerK8sTest (line 285) | type publicKeyPublishHandlerK8sTest struct
function TestPublicKeyPublishHandlerWithK8s (line 296) | func TestPublicKeyPublishHandlerWithK8s(t *testing.T) {
function runPublicKeyPublishHandlerWithK8sCase (line 335) | func runPublicKeyPublishHandlerWithK8sCase(t *testing.T, test *publicKey...
type isValidPublicKeyTest (line 406) | type isValidPublicKeyTest struct
function TestIsValidPublicKey (line 412) | func TestIsValidPublicKey(t *testing.T) {
type isValidTokenTest (line 430) | type isValidTokenTest struct
function TestIsValidToken (line 436) | func TestIsValidToken(t *testing.T) {
type isValidJWTTest (line 462) | type isValidJWTTest struct
function TestIsValidJWT (line 468) | func TestIsValidJWT(t *testing.T) {
type tokenFromRequestTest (line 494) | type tokenFromRequestTest struct
function TestTokenFromRequest (line 502) | func TestTokenFromRequest(t *testing.T) {
type VerifyTokenHandlerTest (line 567) | type VerifyTokenHandlerTest struct
function TestVerifyTokenHandler (line 580) | func TestVerifyTokenHandler(t *testing.T) {
function runVerifyTokenHandlerTest (line 600) | func runVerifyTokenHandlerTest(t *testing.T, test *VerifyTokenHandlerTes...
type TokenOAuth2HandlerTest (line 661) | type TokenOAuth2HandlerTest struct
function TestTokenOAuth2HandlerHapyPath (line 688) | func TestTokenOAuth2HandlerHapyPath(t *testing.T) {
function TestTokenOAuth2HandlerDifferentPrivateKey (line 694) | func TestTokenOAuth2HandlerDifferentPrivateKey(t *testing.T) {
function TestTokenOAuth2HandlerWrongAud (line 706) | func TestTokenOAuth2HandlerWrongAud(t *testing.T) {
function TestTokenOAuth2HandlerExpired (line 717) | func TestTokenOAuth2HandlerExpired(t *testing.T) {
function runTokenOAuth2HandlerTestWithK8s (line 729) | func runTokenOAuth2HandlerTestWithK8s(t *testing.T, test TokenOAuth2Hand...
function Test_verifyJWTHandler (line 813) | func Test_verifyJWTHandler(t *testing.T) {
FILE: src/go/cmd/token-vendor/app/tokenvendor.go
type TokenVendor (line 37) | type TokenVendor struct
method PublishPublicKey (line 52) | func (tv *TokenVendor) PublishPublicKey(ctx context.Context, deviceID,...
method ReadPublicKey (line 57) | func (tv *TokenVendor) ReadPublicKey(ctx context.Context, deviceID str...
method ConfigurePublicKey (line 66) | func (tv *TokenVendor) ConfigurePublicKey(ctx context.Context, deviceI...
method GetOAuth2Token (line 112) | func (tv *TokenVendor) GetOAuth2Token(ctx context.Context, jwtk string...
method ValidateJWT (line 141) | func (tv *TokenVendor) ValidateJWT(ctx context.Context, jwtk string) (...
method getOAuth2Token (line 184) | func (tv *TokenVendor) getOAuth2Token(ctx context.Context, jwtk string...
method VerifyToken (line 273) | func (tv *TokenVendor) VerifyToken(ctx context.Context, token oauth.To...
function NewTokenVendor (line 45) | func NewTokenVendor(ctx context.Context, repo repository.PubKeyRepositor...
function validateKeyOptions (line 75) | func validateKeyOptions(opts repository.KeyOptions) error {
function validateEmail (line 90) | func validateEmail(e string) error {
type DeviceAuth (line 128) | type DeviceAuth struct
function acceptedAudience (line 224) | func acceptedAudience(aud, accAud string) error {
function contains (line 233) | func contains(s []string, str string) bool {
function init (line 259) | func init() {
function IsValidDeviceID (line 304) | func IsValidDeviceID(ID string) bool {
FILE: src/go/cmd/token-vendor/app/tokenvendor_test.go
type isValidDeviceIDTest (line 19) | type isValidDeviceIDTest struct
function TestValidateDeviceId (line 24) | func TestValidateDeviceId(t *testing.T) {
type acceptedAudienceTest (line 44) | type acceptedAudienceTest struct
function TestAcceptedAudience (line 51) | func TestAcceptedAudience(t *testing.T) {
type serviceAccountNameTest (line 73) | type serviceAccountNameTest struct
type keyOptionsTest (line 81) | type keyOptionsTest struct
function TestKeyOptions (line 87) | func TestKeyOptions(t *testing.T) {
function TestTokenVendor_ValidateJWT (line 127) | func TestTokenVendor_ValidateJWT(t *testing.T) {
function getInMemoryRepo (line 263) | func getInMemoryRepo(deviceId, key string) repository.PubKeyRepository {
function createFakeJWTWithSubject (line 270) | func createFakeJWTWithSubject(t *testing.T, deviceId, subject string) st...
function createSAFromName (line 291) | func createSAFromName(name string) string {
FILE: src/go/cmd/token-vendor/main.go
type scopeFlags (line 42) | type scopeFlags
method String (line 44) | func (i *scopeFlags) String() string {
method Set (line 48) | func (i *scopeFlags) Set(value string) error {
type KeyStoreOpt (line 53) | type KeyStoreOpt
constant Kubernetes (line 56) | Kubernetes = "KUBERNETES"
constant Memory (line 57) | Memory = "IN_MEMORY"
function main (line 95) | func main() {
FILE: src/go/cmd/token-vendor/oauth/cache.go
type entry (line 10) | type entry struct
type tokenCache (line 19) | type tokenCache struct
method add (line 50) | func (c *tokenCache) add(token Token, sa string, actAs bool) {
method evictExpired (line 76) | func (c *tokenCache) evictExpired() {
method evictOldest (line 88) | func (c *tokenCache) evictOldest() {
method actAs (line 102) | func (c *tokenCache) actAs(token Token, sa string) (bool, bool) {
function newTokenCache (line 36) | func newTokenCache(size int, expire time.Duration) (*tokenCache, error) {
FILE: src/go/cmd/token-vendor/oauth/cache_test.go
type tokenProp (line 11) | type tokenProp struct
function randToken (line 22) | func randToken() *tokenProp {
function TestTokenCacheOneTokenMultipleAcl (line 31) | func TestTokenCacheOneTokenMultipleAcl(t *testing.T) {
function TestTokenCacheExpire (line 48) | func TestTokenCacheExpire(t *testing.T) {
function TestEvictExpired (line 62) | func TestEvictExpired(t *testing.T) {
function TestEvictOldest (line 78) | func TestEvictOldest(t *testing.T) {
function TestEvictionGeneral (line 98) | func TestEvictionGeneral(t *testing.T) {
function TestParallelAccessNoEviction (line 116) | func TestParallelAccessNoEviction(t *testing.T) {
function accessWorker (line 144) | func accessWorker(tc *tokenCache, adds int, failIfNotFound bool) error {
FILE: src/go/cmd/token-vendor/oauth/jwt/jwt.go
type payload (line 12) | type payload struct
function PayloadUnsafe (line 25) | func PayloadUnsafe(jwtk string) (*payload, error) {
function VerifySignature (line 43) | func VerifySignature(jwtk string, pubKey string) error {
FILE: src/go/cmd/token-vendor/oauth/jwt/jwt_test.go
constant testJWT (line 39) | testJWT = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJ0ZXN0YXVkIiwi...
constant testPubKey (line 41) | testPubKey = `
function TestVerifySignature (line 85) | func TestVerifySignature(t *testing.T) {
function TestPayloadUnsafe (line 92) | func TestPayloadUnsafe(t *testing.T) {
function TestPayloadUnsafeInvalidPayload (line 102) | func TestPayloadUnsafeInvalidPayload(t *testing.T) {
function TestVerifySignatureInvalidSig (line 111) | func TestVerifySignatureInvalidSig(t *testing.T) {
constant testJWTInvalidSigAlg (line 125) | testJWTInvalidSigAlg = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJ...
function TestVerifySignatureInvalidSigAlg (line 127) | func TestVerifySignatureInvalidSigAlg(t *testing.T) {
FILE: src/go/cmd/token-vendor/oauth/verifier.go
type TokenVerifier (line 14) | type TokenVerifier struct
method Verify (line 42) | func (v *TokenVerifier) Verify(ctx context.Context, token Token, sa st...
type Token (line 21) | type Token
constant cacheSize (line 24) | cacheSize = 1000
constant cacheExpire (line 25) | cacheExpire = 5 * time.Minute
function NewTokenVerifier (line 29) | func NewTokenVerifier(ctx context.Context, c *http.Client, project strin...
constant timeout (line 71) | timeout = time.Second * 5
constant retryInterval (line 72) | retryInterval = time.Second * 1
constant retries (line 73) | retries = 2
function doTestIamPermissions (line 76) | func doTestIamPermissions(ctx context.Context, s *iam.Service, token, re...
function contains (line 107) | func contains(s []string, str string) bool {
FILE: src/go/cmd/token-vendor/repository/k8s/k8s.go
constant resyncPeriod (line 40) | resyncPeriod = 1 * time.Hour
type K8sRepository (line 43) | type K8sRepository struct
method ListAllDeviceIDs (line 88) | func (k *K8sRepository) ListAllDeviceIDs(ctx context.Context) ([]strin...
method LookupKey (line 104) | func (k *K8sRepository) LookupKey(ctx context.Context, deviceID string...
method PublishKey (line 131) | func (k *K8sRepository) PublishKey(ctx context.Context, deviceID, publ...
method ConfigureKey (line 161) | func (k *K8sRepository) ConfigureKey(ctx context.Context, deviceID str...
function NewK8sRepository (line 55) | func NewK8sRepository(ctx context.Context, kcl kubernetes.Interface, ns ...
constant pubKey (line 80) | pubKey = "pubKey"
constant serviceAccountAnnotation (line 82) | serviceAccountAnnotation = "cloudrobotics.com/gcp-service-account"
constant serviceAccountDelegateAnnotation (line 84) | serviceAccountDelegateAnnotation = "cloudrobotics.com/gcp-service-accoun...
function createPubKeyDeviceConfig (line 188) | func createPubKeyDeviceConfig(name, namespace, pk string) (*corev1.Confi...
function mapSetOrDelete (line 205) | func mapSetOrDelete(m map[string]string, k, v string) {
FILE: src/go/cmd/token-vendor/repository/k8s/k8s_test.go
function TestPublishListLookup (line 28) | func TestPublishListLookup(t *testing.T) {
function TestPublishKeyUpdate (line 53) | func TestPublishKeyUpdate(t *testing.T) {
function TestLookupDoesNotExist (line 77) | func TestLookupDoesNotExist(t *testing.T) {
function TestConfigure (line 93) | func TestConfigure(t *testing.T) {
function TestReConfigure (line 118) | func TestReConfigure(t *testing.T) {
FILE: src/go/cmd/token-vendor/repository/memory/memory.go
type MemoryRepository (line 26) | type MemoryRepository struct
method PublishKey (line 38) | func (m *MemoryRepository) PublishKey(ctx context.Context, deviceID, p...
method LookupKey (line 44) | func (m *MemoryRepository) LookupKey(ctx context.Context, deviceID str...
method ConfigureKey (line 58) | func (m *MemoryRepository) ConfigureKey(ctx context.Context, deviceID ...
function NewMemoryRepository (line 31) | func NewMemoryRepository(ctx context.Context) (*MemoryRepository, error) {
FILE: src/go/cmd/token-vendor/repository/memory/memory_test.go
function TestPublishAndLookup (line 26) | func TestPublishAndLookup(t *testing.T) {
function TestLookupNotFound (line 54) | func TestLookupNotFound(t *testing.T) {
function TestConfigure (line 69) | func TestConfigure(t *testing.T) {
FILE: src/go/cmd/token-vendor/repository/repository.go
type Key (line 29) | type Key struct
type KeyOptions (line 39) | type KeyOptions struct
type PubKeyRepository (line 45) | type PubKeyRepository interface
FILE: src/go/cmd/token-vendor/tokensource/gcp.go
type GCPTokenSource (line 19) | type GCPTokenSource struct
method Token (line 53) | func (g *GCPTokenSource) Token(ctx context.Context, saName, saDelegate...
type TokenResponse (line 24) | type TokenResponse struct
constant saPrefix (line 32) | saPrefix = "projects/-/serviceAccounts/"
function NewGCPTokenSource (line 42) | func NewGCPTokenSource(ctx context.Context, client *http.Client, scopes ...
function tokenResponse (line 87) | func tokenResponse(r *iam.GenerateAccessTokenResponse, scopes []string, ...
function getWorkloadServiceAccount (line 107) | func getWorkloadServiceAccount(ctx context.Context) (string, error) {
FILE: src/go/cmd/token-vendor/tokensource/gcp_test.go
type TokenResponseTest (line 11) | type TokenResponseTest struct
function TestTokenResponse (line 20) | func TestTokenResponse(t *testing.T) {
FILE: src/go/pkg/apis/apps/v1alpha1/register.go
function init (line 33) | func init() {
function Resource (line 41) | func Resource(resource string) schema.GroupResource {
function addKnownTypes (line 46) | func addKnownTypes(scheme *runtime.Scheme) error {
FILE: src/go/pkg/apis/apps/v1alpha1/types.go
type ResourceSet (line 27) | type ResourceSet struct
type ResourceSetList (line 37) | type ResourceSetList struct
type ResourceSetSpec (line 44) | type ResourceSetSpec struct
type ResourceSetStatus (line 48) | type ResourceSetStatus struct
type ResourceSetSpecGroup (line 56) | type ResourceSetSpecGroup struct
type ResourceSetStatusGroup (line 63) | type ResourceSetStatusGroup struct
type ResourceRef (line 70) | type ResourceRef struct
type ResourceStatus (line 75) | type ResourceStatus struct
type ResourceSetPhase (line 84) | type ResourceSetPhase
constant ResourceSetPhasePending (line 87) | ResourceSetPhasePending ResourceSetPhase = "Pending"
constant ResourceSetPhaseFailed (line 88) | ResourceSetPhaseFailed ResourceSetPhase = "Failed"
constant ResourceSetPhaseSettled (line 89) | ResourceSetPhaseSettled ResourceSetPhase = "Settled"
type ResourceAction (line 92) | type ResourceAction
constant ResourceActionNone (line 95) | ResourceActionNone ResourceAction = "None"
constant ResourceActionCreate (line 96) | ResourceActionCreate ResourceAction = "Create"
constant ResourceActionUpdate (line 97) | ResourceActionUpdate ResourceAction = "Update"
constant ResourceActionReplace (line 98) | ResourceActionReplace ResourceAction = "Replace"
type App (line 105) | type App struct
type AppList (line 114) | type AppList struct
type AppSpec (line 121) | type AppSpec struct
type AppComponents (line 127) | type AppComponents struct
type AppComponent (line 132) | type AppComponent struct
type AppRollout (line 141) | type AppRollout struct
type AppRolloutList (line 151) | type AppRolloutList struct
type AppRolloutSpec (line 158) | type AppRolloutSpec struct
type AppRolloutSpecCloud (line 164) | type AppRolloutSpecCloud struct
type AppRolloutSpecRobot (line 168) | type AppRolloutSpecRobot struct
type RobotSelector (line 175) | type RobotSelector struct
type AppRolloutStatus (line 181) | type AppRolloutStatus struct
type AppRolloutCondition (line 190) | type AppRolloutCondition struct
type AppRolloutConditionType (line 198) | type AppRolloutConditionType
constant AppRolloutConditionSettled (line 201) | AppRolloutConditionSettled AppRolloutConditionType = "Settled"
constant AppRolloutConditionReady (line 202) | AppRolloutConditionReady AppRolloutConditionType = "Ready"
type ChartAssignment (line 209) | type ChartAssignment struct
type ChartAssignmentList (line 219) | type ChartAssignmentList struct
type ChartAssignmentSpec (line 226) | type ChartAssignmentSpec struct
type AssignedChart (line 232) | type AssignedChart struct
type ConfigValues (line 240) | type ConfigValues
method DeepCopy (line 244) | func (in ConfigValues) DeepCopy() ConfigValues {
type ChartAssignmentStatus (line 248) | type ChartAssignmentStatus struct
type ChartAssignmentPhase (line 254) | type ChartAssignmentPhase
constant ChartAssignmentPhaseAccepted (line 259) | ChartAssignmentPhaseAccepted ChartAssignmentPhase = "Accepted"
constant ChartAssignmentPhaseLoadingChart (line 261) | ChartAssignmentPhaseLoadingChart ChartAssignmentPhase = "LoadingChart"
constant ChartAssignmentPhaseInstalling (line 263) | ChartAssignmentPhaseInstalling ChartAssignmentPhase = "Installing"
constant ChartAssignmentPhaseUpdating (line 265) | ChartAssignmentPhaseUpdating ChartAssignmentPhase = "Updating"
constant ChartAssignmentPhaseDeleting (line 268) | ChartAssignmentPhaseDeleting ChartAssignmentPhase = "Deleting"
constant ChartAssignmentPhaseSettled (line 270) | ChartAssignmentPhaseSettled ChartAssignmentPhase = "Settled"
constant ChartAssignmentPhaseDeleted (line 272) | ChartAssignmentPhaseDeleted ChartAssignmentPhase = "Deleted"
constant ChartAssignmentPhaseFailed (line 276) | ChartAssignmentPhaseFailed ChartAssignmentPhase = "Failed"
constant ChartAssignmentPhaseReady (line 279) | ChartAssignmentPhaseReady ChartAssignmentPhase = "Ready"
type ChartAssignmentCondition (line 282) | type ChartAssignmentCondition struct
type ChartAssignmentConditionType (line 290) | type ChartAssignmentConditionType
constant ChartAssignmentConditionSettled (line 293) | ChartAssignmentConditionSettled ChartAssignmentConditionType = "Settled"
constant ChartAssignmentConditionReady (line 294) | ChartAssignmentConditionReady ChartAssignmentConditionType = "Ready"
FILE: src/go/pkg/apis/apps/v1alpha1/zz_generated.deepcopy.go
method DeepCopyInto (line 28) | func (in *App) DeepCopyInto(out *App) {
method DeepCopy (line 37) | func (in *App) DeepCopy() *App {
method DeepCopyObject (line 47) | func (in *App) DeepCopyObject() runtime.Object {
method DeepCopyInto (line 55) | func (in *AppComponent) DeepCopyInto(out *AppComponent) {
method DeepCopy (line 61) | func (in *AppComponent) DeepCopy() *AppComponent {
method DeepCopyInto (line 71) | func (in *AppComponents) DeepCopyInto(out *AppComponents) {
method DeepCopy (line 79) | func (in *AppComponents) DeepCopy() *AppComponents {
method DeepCopyInto (line 89) | func (in *AppList) DeepCopyInto(out *AppList) {
method DeepCopy (line 104) | func (in *AppList) DeepCopy() *AppList {
method DeepCopyObject (line 114) | func (in *AppList) DeepCopyObject() runtime.Object {
method DeepCopyInto (line 122) | func (in *AppRollout) DeepCopyInto(out *AppRollout) {
method DeepCopy (line 132) | func (in *AppRollout) DeepCopy() *AppRollout {
method DeepCopyObject (line 142) | func (in *AppRollout) DeepCopyObject() runtime.Object {
method DeepCopyInto (line 150) | func (in *AppRolloutCondition) DeepCopyInto(out *AppRolloutCondition) {
method DeepCopy (line 158) | func (in *AppRolloutCondition) DeepCopy() *AppRolloutCondition {
method DeepCopyInto (line 168) | func (in *AppRolloutList) DeepCopyInto(out *AppRolloutList) {
method DeepCopy (line 183) | func (in *AppRolloutList) DeepCopy() *AppRolloutList {
method DeepCopyObject (line 193) | func (in *AppRolloutList) DeepCopyObject() runtime.Object {
method DeepCopyInto (line 201) | func (in *AppRolloutSpec) DeepCopyInto(out *AppRolloutSpec) {
method DeepCopy (line 215) | func (in *AppRolloutSpec) DeepCopy() *AppRolloutSpec {
method DeepCopyInto (line 225) | func (in *AppRolloutSpecCloud) DeepCopyInto(out *AppRolloutSpecCloud) {
method DeepCopy (line 232) | func (in *AppRolloutSpecCloud) DeepCopy() *AppRolloutSpecCloud {
method DeepCopyInto (line 242) | func (in *AppRolloutSpecRobot) DeepCopyInto(out *AppRolloutSpecRobot) {
method DeepCopy (line 254) | func (in *AppRolloutSpecRobot) DeepCopy() *AppRolloutSpecRobot {
method DeepCopyInto (line 264) | func (in *AppRolloutStatus) DeepCopyInto(out *AppRolloutStatus) {
method DeepCopy (line 277) | func (in *AppRolloutStatus) DeepCopy() *AppRolloutStatus {
method DeepCopyInto (line 287) | func (in *AppSpec) DeepCopyInto(out *AppSpec) {
method DeepCopy (line 294) | func (in *AppSpec) DeepCopy() *AppSpec {
method DeepCopyInto (line 304) | func (in *AssignedChart) DeepCopyInto(out *AssignedChart) {
method DeepCopy (line 311) | func (in *AssignedChart) DeepCopy() *AssignedChart {
method DeepCopyInto (line 321) | func (in *ChartAssignment) DeepCopyInto(out *ChartAssignment) {
method DeepCopy (line 331) | func (in *ChartAssignment) DeepCopy() *ChartAssignment {
method DeepCopyObject (line 341) | func (in *ChartAssignment) DeepCopyObject() runtime.Object {
method DeepCopyInto (line 349) | func (in *ChartAssignmentCondition) DeepCopyInto(out *ChartAssignmentCon...
method DeepCopy (line 357) | func (in *ChartAssignmentCondition) DeepCopy() *ChartAssignmentCondition {
method DeepCopyInto (line 367) | func (in *ChartAssignmentList) DeepCopyInto(out *ChartAssignmentList) {
method DeepCopy (line 382) | func (in *ChartAssignmentList) DeepCopy() *ChartAssignmentList {
method DeepCopyObject (line 392) | func (in *ChartAssignmentList) DeepCopyObject() runtime.Object {
method DeepCopyInto (line 400) | func (in *ChartAssignmentSpec) DeepCopyInto(out *ChartAssignmentSpec) {
method DeepCopy (line 407) | func (in *ChartAssignmentSpec) DeepCopy() *ChartAssignmentSpec {
method DeepCopyInto (line 417) | func (in *ChartAssignmentStatus) DeepCopyInto(out *ChartAssignmentStatus) {
method DeepCopy (line 430) | func (in *ChartAssignmentStatus) DeepCopy() *ChartAssignmentStatus {
method DeepCopyInto (line 440) | func (in ConfigValues) DeepCopyInto(out *ConfigValues) {
method DeepCopyInto (line 449) | func (in *ResourceRef) DeepCopyInto(out *ResourceRef) {
method DeepCopy (line 455) | func (in *ResourceRef) DeepCopy() *ResourceRef {
method DeepCopyInto (line 465) | func (in *ResourceSet) DeepCopyInto(out *ResourceSet) {
method DeepCopy (line 475) | func (in *ResourceSet) DeepCopy() *ResourceSet {
method DeepCopyObject (line 485) | func (in *ResourceSet) DeepCopyObject() runtime.Object {
method DeepCopyInto (line 493) | func (in *ResourceSetList) DeepCopyInto(out *ResourceSetList) {
method DeepCopy (line 508) | func (in *ResourceSetList) DeepCopy() *ResourceSetList {
method DeepCopyObject (line 518) | func (in *ResourceSetList) DeepCopyObject() runtime.Object {
method DeepCopyInto (line 526) | func (in *ResourceSetSpec) DeepCopyInto(out *ResourceSetSpec) {
method DeepCopy (line 539) | func (in *ResourceSetSpec) DeepCopy() *ResourceSetSpec {
method DeepCopyInto (line 549) | func (in *ResourceSetSpecGroup) DeepCopyInto(out *ResourceSetSpecGroup) {
method DeepCopy (line 560) | func (in *ResourceSetSpecGroup) DeepCopy() *ResourceSetSpecGroup {
method DeepCopyInto (line 570) | func (in *ResourceSetStatus) DeepCopyInto(out *ResourceSetStatus) {
method DeepCopy (line 592) | func (in *ResourceSetStatus) DeepCopy() *ResourceSetStatus {
method DeepCopyInto (line 602) | func (in *ResourceSetStatusGroup) DeepCopyInto(out *ResourceSetStatusGro...
method DeepCopy (line 613) | func (in *ResourceSetStatusGroup) DeepCopy() *ResourceSetStatusGroup {
method DeepCopyInto (line 623) | func (in *ResourceStatus) DeepCopyInto(out *ResourceStatus) {
method DeepCopy (line 629) | func (in *ResourceStatus) DeepCopy() *ResourceStatus {
method DeepCopyInto (line 639) | func (in *RobotSelector) DeepCopyInto(out *RobotSelector) {
method DeepCopy (line 655) | func (in *RobotSelector) DeepCopy() *RobotSelector {
FILE: src/go/pkg/apis/registry/v1alpha1/register.go
function init (line 31) | func init() {
function Resource (line 36) | func Resource(resource string) schema.GroupResource {
function addKnownTypes (line 41) | func addKnownTypes(scheme *runtime.Scheme) error {
FILE: src/go/pkg/apis/registry/v1alpha1/types.go
type Robot (line 24) | type Robot struct
type RobotList (line 34) | type RobotList struct
type RobotSpec (line 41) | type RobotSpec struct
type RobotStatus (line 46) | type RobotStatus struct
type RobotStatusCloud (line 52) | type RobotStatusCloud struct
type RobotStatusRobot (line 55) | type RobotStatusRobot struct
type RobotConfiguration (line 65) | type RobotConfiguration struct
type RobotState (line 69) | type RobotState
constant RobotStateUndefined (line 72) | RobotStateUndefined RobotState = "UNDEFINED"
constant RobotStateUnavailable (line 73) | RobotStateUnavailable RobotState = "UNAVAILABLE"
constant RobotStateAvailable (line 74) | RobotStateAvailable RobotState = "AVAILABLE"
constant RobotStateEmergencyStop (line 75) | RobotStateEmergencyStop RobotState = "EMERGENCY_STOP"
constant RobotStateError (line 76) | RobotStateError RobotState = "ERROR"
FILE: src/go/pkg/apis/registry/v1alpha1/zz_generated.deepcopy.go
method DeepCopyInto (line 27) | func (in *Robot) DeepCopyInto(out *Robot) {
method DeepCopy (line 37) | func (in *Robot) DeepCopy() *Robot {
method DeepCopyObject (line 47) | func (in *Robot) DeepCopyObject() runtime.Object {
method DeepCopyInto (line 55) | func (in *RobotConfiguration) DeepCopyInto(out *RobotConfiguration) {
method DeepCopy (line 61) | func (in *RobotConfiguration) DeepCopy() *RobotConfiguration {
method DeepCopyInto (line 71) | func (in *RobotList) DeepCopyInto(out *RobotList) {
method DeepCopy (line 86) | func (in *RobotList) DeepCopy() *RobotList {
method DeepCopyObject (line 96) | func (in *RobotList) DeepCopyObject() runtime.Object {
method DeepCopyInto (line 104) | func (in *RobotSpec) DeepCopyInto(out *RobotSpec) {
method DeepCopy (line 110) | func (in *RobotSpec) DeepCopy() *RobotSpec {
method DeepCopyInto (line 120) | func (in *RobotStatus) DeepCopyInto(out *RobotStatus) {
method DeepCopy (line 129) | func (in *RobotStatus) DeepCopy() *RobotStatus {
method DeepCopyInto (line 139) | func (in *RobotStatusCloud) DeepCopyInto(out *RobotStatusCloud) {
method DeepCopy (line 145) | func (in *RobotStatusCloud) DeepCopy() *RobotStatusCloud {
method DeepCopyInto (line 155) | func (in *RobotStatusRobot) DeepCopyInto(out *RobotStatusRobot) {
method DeepCopy (line 170) | func (in *RobotStatusRobot) DeepCopy() *RobotStatusRobot {
FILE: src/go/pkg/client/informers/apps/interface.go
type Interface (line 25) | type Interface interface
type group (line 30) | type group struct
method V1alpha1 (line 42) | func (g *group) V1alpha1() v1alpha1.Interface {
function New (line 37) | func New(f internalinterfaces.SharedInformerFactory, namespace string, t...
FILE: src/go/pkg/client/informers/apps/v1alpha1/app.go
type AppInformer (line 35) | type AppInformer interface
type appInformer (line 40) | type appInformer struct
method defaultInformer (line 77) | func (f *appInformer) defaultInformer(client versioned.Interface, resy...
method Informer (line 81) | func (f *appInformer) Informer() cache.SharedIndexInformer {
method Lister (line 85) | func (f *appInformer) Lister() v1alpha1.AppLister {
function NewAppInformer (line 48) | func NewAppInformer(client versioned.Interface, resyncPeriod time.Durati...
function NewFilteredAppInformer (line 55) | func NewFilteredAppInformer(client versioned.Interface, resyncPeriod tim...
FILE: src/go/pkg/client/informers/apps/v1alpha1/approllout.go
type AppRolloutInformer (line 35) | type AppRolloutInformer interface
type appRolloutInformer (line 40) | type appRolloutInformer struct
method defaultInformer (line 77) | func (f *appRolloutInformer) defaultInformer(client versioned.Interfac...
method Informer (line 81) | func (f *appRolloutInformer) Informer() cache.SharedIndexInformer {
method Lister (line 85) | func (f *appRolloutInformer) Lister() v1alpha1.AppRolloutLister {
function NewAppRolloutInformer (line 48) | func NewAppRolloutInformer(client versioned.Interface, resyncPeriod time...
function NewFilteredAppRolloutInformer (line 55) | func NewFilteredAppRolloutInformer(client versioned.Interface, resyncPer...
FILE: src/go/pkg/client/informers/apps/v1alpha1/chartassignment.go
type ChartAssignmentInformer (line 35) | type ChartAssignmentInformer interface
type chartAssignmentInformer (line 40) | type chartAssignmentInformer struct
method defaultInformer (line 77) | func (f *chartAssignmentInformer) defaultInformer(client versioned.Int...
method Informer (line 81) | func (f *chartAssignmentInformer) Informer() cache.SharedIndexInformer {
method Lister (line 85) | func (f *chartAssignmentInformer) Lister() v1alpha1.ChartAssignmentLis...
function NewChartAssignmentInformer (line 48) | func NewChartAssignmentInformer(client versioned.Interface, resyncPeriod...
function NewFilteredChartAssignmentInformer (line 55) | func NewFilteredChartAssignmentInformer(client versioned.Interface, resy...
FILE: src/go/pkg/client/informers/apps/v1alpha1/interface.go
type Interface (line 24) | type Interface interface
type version (line 35) | type version struct
method Apps (line 47) | func (v *version) Apps() AppInformer {
method AppRollouts (line 52) | func (v *version) AppRollouts() AppRolloutInformer {
method ChartAssignments (line 57) | func (v *version) ChartAssignments() ChartAssignmentInformer {
method ResourceSets (line 62) | func (v *version) ResourceSets() ResourceSetInformer {
function New (line 42) | func New(f internalinterfaces.SharedInformerFactory, namespace string, t...
FILE: src/go/pkg/client/informers/apps/v1alpha1/resourceset.go
type ResourceSetInformer (line 35) | type ResourceSetInformer interface
type resourceSetInformer (line 40) | type resourceSetInformer struct
method defaultInformer (line 77) | func (f *resourceSetInformer) defaultInformer(client versioned.Interfa...
method Informer (line 81) | func (f *resourceSetInformer) Informer() cache.SharedIndexInformer {
method Lister (line 85) | func (f *resourceSetInformer) Lister() v1alpha1.ResourceSetLister {
function NewResourceSetInformer (line 48) | func NewResourceSetInformer(client versioned.Interface, resyncPeriod tim...
function NewFilteredResourceSetInformer (line 55) | func NewFilteredResourceSetInformer(client versioned.Interface, resyncPe...
FILE: src/go/pkg/client/informers/factory.go
type SharedInformerOption (line 35) | type SharedInformerOption
type sharedInformerFactory (line 37) | type sharedInformerFactory struct
method Start (line 110) | func (f *sharedInformerFactory) Start(stopCh <-chan struct{}) {
method WaitForCacheSync (line 123) | func (f *sharedInformerFactory) WaitForCacheSync(stopCh <-chan struct{...
method InformerFor (line 146) | func (f *sharedInformerFactory) InformerFor(obj runtime.Object, newFun...
method Apps (line 178) | func (f *sharedInformerFactory) Apps() apps.Interface {
method Registry (line 182) | func (f *sharedInformerFactory) Registry() registry.Interface {
function WithCustomResyncConfig (line 52) | func WithCustomResyncConfig(resyncConfig map[v1.Object]time.Duration) Sh...
function WithTweakListOptions (line 62) | func WithTweakListOptions(tweakListOptions internalinterfaces.TweakListO...
function WithNamespace (line 70) | func WithNamespace(namespace string) SharedInformerOption {
function NewSharedInformerFactory (line 78) | func NewSharedInformerFactory(client versioned.Interface, defaultResync ...
function NewFilteredSharedInformerFactory (line 86) | func NewFilteredSharedInformerFactory(client versioned.Interface, defaul...
function NewSharedInformerFactoryWithOptions (line 91) | func NewSharedInformerFactoryWithOptions(client versioned.Interface, def...
type SharedInformerFactory (line 169) | type SharedInformerFactory interface
FILE: src/go/pkg/client/informers/generic.go
type GenericInformer (line 30) | type GenericInformer interface
type genericInformer (line 35) | type genericInformer struct
method Informer (line 41) | func (f *genericInformer) Informer() cache.SharedIndexInformer {
method Lister (line 46) | func (f *genericInformer) Lister() cache.GenericLister {
method ForResource (line 52) | func (f *sharedInformerFactory) ForResource(resource schema.GroupVersion...
FILE: src/go/pkg/client/informers/internalinterfaces/factory_interfaces.go
type NewInformerFunc (line 29) | type NewInformerFunc
type SharedInformerFactory (line 32) | type SharedInformerFactory interface
type TweakListOptionsFunc (line 38) | type TweakListOptionsFunc
FILE: src/go/pkg/client/informers/registry/interface.go
type Interface (line 25) | type Interface interface
type group (line 30) | type group struct
method V1alpha1 (line 42) | func (g *group) V1alpha1() v1alpha1.Interface {
function New (line 37) | func New(f internalinterfaces.SharedInformerFactory, namespace string, t...
FILE: src/go/pkg/client/informers/registry/v1alpha1/interface.go
type Interface (line 24) | type Interface interface
type version (line 29) | type version struct
method Robots (line 41) | func (v *version) Robots() RobotInformer {
function New (line 36) | func New(f internalinterfaces.SharedInformerFactory, namespace string, t...
FILE: src/go/pkg/client/informers/registry/v1alpha1/robot.go
type RobotInformer (line 35) | type RobotInformer interface
type robotInformer (line 40) | type robotInformer struct
method defaultInformer (line 78) | func (f *robotInformer) defaultInformer(client versioned.Interface, re...
method Informer (line 82) | func (f *robotInformer) Informer() cache.SharedIndexInformer {
method Lister (line 86) | func (f *robotInformer) Lister() v1alpha1.RobotLister {
function NewRobotInformer (line 49) | func NewRobotInformer(client versioned.Interface, namespace string, resy...
function NewFilteredRobotInformer (line 56) | func NewFilteredRobotInformer(client versioned.Interface, namespace stri...
FILE: src/go/pkg/client/listers/apps/v1alpha1/app.go
type AppLister (line 28) | type AppLister interface
type appLister (line 39) | type appLister struct
method List (line 49) | func (s *appLister) List(selector labels.Selector) (ret []*v1alpha1.Ap...
method Get (line 57) | func (s *appLister) Get(name string) (*v1alpha1.App, error) {
function NewAppLister (line 44) | func NewAppLister(indexer cache.Indexer) AppLister {
FILE: src/go/pkg/client/listers/apps/v1alpha1/approllout.go
type AppRolloutLister (line 28) | type AppRolloutLister interface
type appRolloutLister (line 39) | type appRolloutLister struct
method List (line 49) | func (s *appRolloutLister) List(selector labels.Selector) (ret []*v1al...
method Get (line 57) | func (s *appRolloutLister) Get(name string) (*v1alpha1.AppRollout, err...
function NewAppRolloutLister (line 44) | func NewAppRolloutLister(indexer cache.Indexer) AppRolloutLister {
FILE: src/go/pkg/client/listers/apps/v1alpha1/chartassignment.go
type ChartAssignmentLister (line 28) | type ChartAssignmentLister interface
type chartAssignmentLister (line 39) | type chartAssignmentLister struct
method List (line 49) | func (s *chartAssignmentLister) List(selector labels.Selector) (ret []...
method Get (line 57) | func (s *chartAssignmentLister) Get(name string) (*v1alpha1.ChartAssig...
function NewChartAssignmentLister (line 44) | func NewChartAssignmentLister(indexer cache.Indexer) ChartAssignmentList...
FILE: src/go/pkg/client/listers/apps/v1alpha1/expansion_generated.go
type AppListerExpansion (line 21) | type AppListerExpansion interface
type AppRolloutListerExpansion (line 25) | type AppRolloutListerExpansion interface
type ChartAssignmentListerExpansion (line 29) | type ChartAssignmentListerExpansion interface
type ResourceSetListerExpansion (line 33) | type ResourceSetListerExpansion interface
FILE: src/go/pkg/client/listers/apps/v1alpha1/resourceset.go
type ResourceSetLister (line 28) | type ResourceSetLister interface
type resourceSetLister (line 39) | type resourceSetLister struct
method List (line 49) | func (s *resourceSetLister) List(selector labels.Selector) (ret []*v1a...
method Get (line 57) | func (s *resourceSetLister) Get(name string) (*v1alpha1.ResourceSet, e...
function NewResourceSetLister (line 44) | func NewResourceSetLister(indexer cache.Indexer) ResourceSetLister {
FILE: src/go/pkg/client/listers/registry/v1alpha1/expansion_generated.go
type RobotListerExpansion (line 21) | type RobotListerExpansion interface
type RobotNamespaceListerExpansion (line 25) | type RobotNamespaceListerExpansion interface
FILE: src/go/pkg/client/listers/registry/v1alpha1/robot.go
type RobotLister (line 28) | type RobotLister interface
type robotLister (line 38) | type robotLister struct
method List (line 48) | func (s *robotLister) List(selector labels.Selector) (ret []*v1alpha1....
method Robots (line 56) | func (s *robotLister) Robots(namespace string) RobotNamespaceLister {
function NewRobotLister (line 43) | func NewRobotLister(indexer cache.Indexer) RobotLister {
type RobotNamespaceLister (line 62) | type RobotNamespaceLister interface
type robotNamespaceLister (line 74) | type robotNamespaceLister struct
method List (line 80) | func (s robotNamespaceLister) List(selector labels.Selector) (ret []*v...
method Get (line 88) | func (s robotNamespaceLister) Get(name string) (*v1alpha1.Robot, error) {
FILE: src/go/pkg/client/versioned/clientset.go
type Interface (line 29) | type Interface interface
type Clientset (line 37) | type Clientset struct
method AppsV1alpha1 (line 44) | func (c *Clientset) AppsV1alpha1() appsv1alpha1.AppsV1alpha1Interface {
method RegistryV1alpha1 (line 49) | func (c *Clientset) RegistryV1alpha1() registryv1alpha1.RegistryV1alph...
method Discovery (line 54) | func (c *Clientset) Discovery() discovery.DiscoveryInterface {
function NewForConfig (line 64) | func NewForConfig(c *rest.Config) (*Clientset, error) {
function NewForConfigOrDie (line 92) | func NewForConfigOrDie(c *rest.Config) *Clientset {
function New (line 102) | func New(c rest.Interface) *Clientset {
FILE: src/go/pkg/client/versioned/fake/clientset_generated.go
function NewSimpleClientset (line 36) | func NewSimpleClientset(objects ...runtime.Object) *Clientset {
type Clientset (line 63) | type Clientset struct
method Discovery (line 69) | func (c *Clientset) Discovery() discovery.DiscoveryInterface {
method Tracker (line 73) | func (c *Clientset) Tracker() testing.ObjectTracker {
method AppsV1alpha1 (line 83) | func (c *Clientset) AppsV1alpha1() appsv1alpha1.AppsV1alpha1Interface {
method RegistryV1alpha1 (line 88) | func (c *Clientset) RegistryV1alpha1() registryv1alpha1.RegistryV1alph...
FILE: src/go/pkg/client/versioned/fake/register.go
function init (line 53) | func init() {
FILE: src/go/pkg/client/versioned/scheme/register.go
function init (line 53) | func init() {
FILE: src/go/pkg/client/versioned/typed/apps/v1alpha1/app.go
type AppsGetter (line 33) | type AppsGetter interface
type AppInterface (line 38) | type AppInterface interface
type apps (line 51) | type apps struct
method Get (line 63) | func (c *apps) Get(ctx context.Context, name string, options v1.GetOpt...
method List (line 75) | func (c *apps) List(ctx context.Context, opts v1.ListOptions) (result ...
method Watch (line 91) | func (c *apps) Watch(ctx context.Context, opts v1.ListOptions) (watch....
method Create (line 105) | func (c *apps) Create(ctx context.Context, app *v1alpha1.App, opts v1....
method Update (line 117) | func (c *apps) Update(ctx context.Context, app *v1alpha1.App, opts v1....
method Delete (line 130) | func (c *apps) Delete(ctx context.Context, name string, opts v1.Delete...
method DeleteCollection (line 140) | func (c *apps) DeleteCollection(ctx context.Context, opts v1.DeleteOpt...
method Patch (line 155) | func (c *apps) Patch(ctx context.Context, name string, pt types.PatchT...
function newApps (line 56) | func newApps(c *AppsV1alpha1Client) *apps {
FILE: src/go/pkg/client/versioned/typed/apps/v1alpha1/approllout.go
type AppRolloutsGetter (line 33) | type AppRolloutsGetter interface
type AppRolloutInterface (line 38) | type AppRolloutInterface interface
type appRollouts (line 52) | type appRollouts struct
method Get (line 64) | func (c *appRollouts) Get(ctx context.Context, name string, options v1...
method List (line 76) | func (c *appRollouts) List(ctx context.Context, opts v1.ListOptions) (...
method Watch (line 92) | func (c *appRollouts) Watch(ctx context.Context, opts v1.ListOptions) ...
method Create (line 106) | func (c *appRollouts) Create(ctx context.Context, appRollout *v1alpha1...
method Update (line 118) | func (c *appRollouts) Update(ctx context.Context, appRollout *v1alpha1...
method UpdateStatus (line 132) | func (c *appRollouts) UpdateStatus(ctx context.Context, appRollout *v1...
method Delete (line 146) | func (c *appRollouts) Delete(ctx context.Context, name string, opts v1...
method DeleteCollection (line 156) | func (c *appRollouts) DeleteCollection(ctx context.Context, opts v1.De...
method Patch (line 171) | func (c *appRollouts) Patch(ctx context.Context, name string, pt types...
function newAppRollouts (line 57) | func newAppRollouts(c *AppsV1alpha1Client) *appRollouts {
FILE: src/go/pkg/client/versioned/typed/apps/v1alpha1/apps_client.go
type AppsV1alpha1Interface (line 25) | type AppsV1alpha1Interface interface
type AppsV1alpha1Client (line 34) | type AppsV1alpha1Client struct
method Apps (line 38) | func (c *AppsV1alpha1Client) Apps() AppInterface {
method AppRollouts (line 42) | func (c *AppsV1alpha1Client) AppRollouts() AppRolloutInterface {
method ChartAssignments (line 46) | func (c *AppsV1alpha1Client) ChartAssignments() ChartAssignmentInterfa...
method ResourceSets (line 50) | func (c *AppsV1alpha1Client) ResourceSets() ResourceSetInterface {
method RESTClient (line 97) | func (c *AppsV1alpha1Client) RESTClient() rest.Interface {
function NewForConfig (line 55) | func NewForConfig(c *rest.Config) (*AppsV1alpha1Client, error) {
function NewForConfigOrDie (line 69) | func NewForConfigOrDie(c *rest.Config) *AppsV1alpha1Client {
function New (line 78) | func New(c rest.Interface) *AppsV1alpha1Client {
function setConfigDefaults (line 82) | func setConfigDefaults(config *rest.Config) error {
FILE: src/go/pkg/client/versioned/typed/apps/v1alpha1/chartassignment.go
type ChartAssignmentsGetter (line 33) | type ChartAssignmentsGetter interface
type ChartAssignmentInterface (line 38) | type ChartAssignmentInterface interface
type chartAssignments (line 52) | type chartAssignments struct
method Get (line 64) | func (c *chartAssignments) Get(ctx context.Context, name string, optio...
method List (line 76) | func (c *chartAssignments) List(ctx context.Context, opts v1.ListOptio...
method Watch (line 92) | func (c *chartAssignments) Watch(ctx context.Context, opts v1.ListOpti...
method Create (line 106) | func (c *chartAssignments) Create(ctx context.Context, chartAssignment...
method Update (line 118) | func (c *chartAssignments) Update(ctx context.Context, chartAssignment...
method UpdateStatus (line 132) | func (c *chartAssignments) UpdateStatus(ctx context.Context, chartAssi...
method Delete (line 146) | func (c *chartAssignments) Delete(ctx context.Context, name string, op...
method DeleteCollection (line 156) | func (c *chartAssignments) DeleteCollection(ctx context.Context, opts ...
method Patch (line 171) | func (c *chartAssignments) Patch(ctx context.Context, name string, pt ...
function newChartAssignments (line 57) | func newChartAssignments(c *AppsV1alpha1Client) *chartAssignments {
FILE: src/go/pkg/client/versioned/typed/apps/v1alpha1/fake/fake_app.go
type FakeApps (line 32) | type FakeApps struct
method Get (line 41) | func (c *FakeApps) Get(ctx context.Context, name string, options v1.Ge...
method List (line 51) | func (c *FakeApps) List(ctx context.Context, opts v1.ListOptions) (res...
method Watch (line 72) | func (c *FakeApps) Watch(ctx context.Context, opts v1.ListOptions) (wa...
method Create (line 78) | func (c *FakeApps) Create(ctx context.Context, app *v1alpha1.App, opts...
method Update (line 88) | func (c *FakeApps) Update(ctx context.Context, app *v1alpha1.App, opts...
method Delete (line 98) | func (c *FakeApps) Delete(ctx context.Context, name string, opts v1.De...
method DeleteCollection (line 105) | func (c *FakeApps) DeleteCollection(ctx context.Context, opts v1.Delet...
method Patch (line 113) | func (c *FakeApps) Patch(ctx context.Context, name string, pt types.Pa...
FILE: src/go/pkg/client/versioned/typed/apps/v1alpha1/fake/fake_approllout.go
type FakeAppRollouts (line 32) | type FakeAppRollouts struct
method Get (line 41) | func (c *FakeAppRollouts) Get(ctx context.Context, name string, option...
method List (line 51) | func (c *FakeAppRollouts) List(ctx context.Context, opts v1.ListOption...
method Watch (line 72) | func (c *FakeAppRollouts) Watch(ctx context.Context, opts v1.ListOptio...
method Create (line 78) | func (c *FakeAppRollouts) Create(ctx context.Context, appRollout *v1al...
method Update (line 88) | func (c *FakeAppRollouts) Update(ctx context.Context, appRollout *v1al...
method UpdateStatus (line 99) | func (c *FakeAppRollouts) UpdateStatus(ctx context.Context, appRollout...
method Delete (line 109) | func (c *FakeAppRollouts) Delete(ctx context.Context, name string, opt...
method DeleteCollection (line 116) | func (c *FakeAppRollouts) DeleteCollection(ctx context.Context, opts v...
method Patch (line 124) | func (c *FakeAppRollouts) Patch(ctx context.Context, name string, pt t...
FILE: src/go/pkg/client/versioned/typed/apps/v1alpha1/fake/fake_apps_client.go
type FakeAppsV1alpha1 (line 25) | type FakeAppsV1alpha1 struct
method Apps (line 29) | func (c *FakeAppsV1alpha1) Apps() v1alpha1.AppInterface {
method AppRollouts (line 33) | func (c *FakeAppsV1alpha1) AppRollouts() v1alpha1.AppRolloutInterface {
method ChartAssignments (line 37) | func (c *FakeAppsV1alpha1) ChartAssignments() v1alpha1.ChartAssignment...
method ResourceSets (line 41) | func (c *FakeAppsV1alpha1) ResourceSets() v1alpha1.ResourceSetInterface {
method RESTClient (line 47) | func (c *FakeAppsV1alpha1) RESTClient() rest.Interface {
FILE: src/go/pkg/client/versioned/typed/apps/v1alpha1/fake/fake_chartassignment.go
type FakeChartAssignments (line 32) | type FakeChartAssignments struct
method Get (line 41) | func (c *FakeChartAssignments) Get(ctx context.Context, name string, o...
method List (line 51) | func (c *FakeChartAssignments) List(ctx context.Context, opts v1.ListO...
method Watch (line 72) | func (c *FakeChartAssignments) Watch(ctx context.Context, opts v1.List...
method Create (line 78) | func (c *FakeChartAssignments) Create(ctx context.Context, chartAssign...
method Update (line 88) | func (c *FakeChartAssignments) Update(ctx context.Context, chartAssign...
method UpdateStatus (line 99) | func (c *FakeChartAssignments) UpdateStatus(ctx context.Context, chart...
method Delete (line 109) | func (c *FakeChartAssignments) Delete(ctx context.Context, name string...
method DeleteCollection (line 116) | func (c *FakeChartAssignments) DeleteCollection(ctx context.Context, o...
method Patch (line 124) | func (c *FakeChartAssignments) Patch(ctx context.Context, name string,...
FILE: src/go/pkg/client/versioned/typed/apps/v1alpha1/fake/fake_resourceset.go
type FakeResourceSets (line 32) | type FakeResourceSets struct
method Get (line 41) | func (c *FakeResourceSets) Get(ctx context.Context, name string, optio...
method List (line 51) | func (c *FakeResourceSets) List(ctx context.Context, opts v1.ListOptio...
method Watch (line 72) | func (c *FakeResourceSets) Watch(ctx context.Context, opts v1.ListOpti...
method Create (line 78) | func (c *FakeResourceSets) Create(ctx context.Context, resourceSet *v1...
method Update (line 88) | func (c *FakeResourceSets) Update(ctx context.Context, resourceSet *v1...
method UpdateStatus (line 99) | func (c *FakeResourceSets) UpdateStatus(ctx context.Context, resourceS...
method Delete (line 109) | func (c *FakeResourceSets) Delete(ctx context.Context, name string, op...
method DeleteCollection (line 116) | func (c *FakeResourceSets) DeleteCollection(ctx context.Context, opts ...
method Patch (line 124) | func (c *FakeResourceSets) Patch(ctx context.Context, name string, pt ...
FILE: src/go/pkg/client/versioned/typed/apps/v1alpha1/generated_expansion.go
type AppExpansion (line 19) | type AppExpansion interface
type AppRolloutExpansion (line 21) | type AppRolloutExpansion interface
type ChartAssignmentExpansion (line 23) | type ChartAssignmentExpansion interface
type ResourceSetExpansion (line 25) | type ResourceSetExpansion interface
FILE: src/go/pkg/client/versioned/typed/apps/v1alpha1/resourceset.go
type ResourceSetsGetter (line 33) | type ResourceSetsGetter interface
type ResourceSetInterface (line 38) | type ResourceSetInterface interface
type resourceSets (line 52) | type resourceSets struct
method Get (line 64) | func (c *resourceSets) Get(ctx context.Context, name string, options v...
method List (line 76) | func (c *resourceSets) List(ctx context.Context, opts v1.ListOptions) ...
method Watch (line 92) | func (c *resourceSets) Watch(ctx context.Context, opts v1.ListOptions)...
method Create (line 106) | func (c *resourceSets) Create(ctx context.Context, resourceSet *v1alph...
method Update (line 118) | func (c *resourceSets) Update(ctx context.Context, resourceSet *v1alph...
method UpdateStatus (line 132) | func (c *resourceSets) UpdateStatus(ctx context.Context, resourceSet *...
method Delete (line 146) | func (c *resourceSets) Delete(ctx context.Context, name string, opts v...
method DeleteCollection (line 156) | func (c *resourceSets) DeleteCollection(ctx context.Context, opts v1.D...
method Patch (line 171) | func (c *resourceSets) Patch(ctx context.Context, name string, pt type...
function newResourceSets (line 57) | func newResourceSets(c *AppsV1alpha1Client) *resourceSets {
FILE: src/go/pkg/client/versioned/typed/registry/v1alpha1/fake/fake_registry_client.go
type FakeRegistryV1alpha1 (line 25) | type FakeRegistryV1alpha1 struct
method Robots (line 29) | func (c *FakeRegistryV1alpha1) Robots(namespace string) v1alpha1.Robot...
method RESTClient (line 35) | func (c *FakeRegistryV1alpha1) RESTClient() rest.Interface {
FILE: src/go/pkg/client/versioned/typed/registry/v1alpha1/fake/fake_robot.go
type FakeRobots (line 32) | type FakeRobots struct
method Get (line 42) | func (c *FakeRobots) Get(ctx context.Context, name string, options v1....
method List (line 53) | func (c *FakeRobots) List(ctx context.Context, opts v1.ListOptions) (r...
method Watch (line 75) | func (c *FakeRobots) Watch(ctx context.Context, opts v1.ListOptions) (...
method Create (line 82) | func (c *FakeRobots) Create(ctx context.Context, robot *v1alpha1.Robot...
method Update (line 93) | func (c *FakeRobots) Update(ctx context.Context, robot *v1alpha1.Robot...
method UpdateStatus (line 105) | func (c *FakeRobots) UpdateStatus(ctx context.Context, robot *v1alpha1...
method Delete (line 116) | func (c *FakeRobots) Delete(ctx context.Context, name string, opts v1....
method DeleteCollection (line 124) | func (c *FakeRobots) DeleteCollection(ctx context.Context, opts v1.Del...
method Patch (line 132) | func (c *FakeRobots) Patch(ctx context.Context, name string, pt types....
FILE: src/go/pkg/client/versioned/typed/registry/v1alpha1/generated_expansion.go
type RobotExpansion (line 19) | type RobotExpansion interface
FILE: src/go/pkg/client/versioned/typed/registry/v1alpha1/registry_client.go
type RegistryV1alpha1Interface (line 25) | type RegistryV1alpha1Interface interface
type RegistryV1alpha1Client (line 31) | type RegistryV1alpha1Client struct
method Robots (line 35) | func (c *RegistryV1alpha1Client) Robots(namespace string) RobotInterfa...
method RESTClient (line 82) | func (c *RegistryV1alpha1Client) RESTClient() rest.Interface {
function NewForConfig (line 40) | func NewForConfig(c *rest.Config) (*RegistryV1alpha1Client, error) {
function NewForConfigOrDie (line 54) | func NewForConfigOrDie(c *rest.Config) *RegistryV1alpha1Client {
function New (line 63) | func New(c rest.Interface) *RegistryV1alpha1Client {
function setConfigDefaults (line 67) | func setConfigDefaults(config *rest.Config) error {
FILE: src/go/pkg/client/versioned/typed/registry/v1alpha1/robot.go
type RobotsGetter (line 33) | type RobotsGetter interface
type RobotInterface (line 38) | type RobotInterface interface
type robots (line 52) | type robots struct
method Get (line 66) | func (c *robots) Get(ctx context.Context, name string, options v1.GetO...
method List (line 79) | func (c *robots) List(ctx context.Context, opts v1.ListOptions) (resul...
method Watch (line 96) | func (c *robots) Watch(ctx context.Context, opts v1.ListOptions) (watc...
method Create (line 111) | func (c *robots) Create(ctx context.Context, robot *v1alpha1.Robot, op...
method Update (line 124) | func (c *robots) Update(ctx context.Context, robot *v1alpha1.Robot, op...
method UpdateStatus (line 139) | func (c *robots) UpdateStatus(ctx context.Context, robot *v1alpha1.Rob...
method Delete (line 154) | func (c *robots) Delete(ctx context.Context, name string, opts v1.Dele...
method DeleteCollection (line 165) | func (c *robots) DeleteCollection(ctx context.Context, opts v1.DeleteO...
method Patch (line 181) | func (c *robots) Patch(ctx context.Context, name string, pt types.Patc...
function newRobots (line 58) | func newRobots(c *RegistryV1alpha1Client, namespace string) *robots {
FILE: src/go/pkg/configutil/config_reader.go
function bashUnescape (line 36) | func bashUnescape(s string) string {
function getConfigFromReader (line 53) | func getConfigFromReader(reader io.Reader) (map[string]string, error) {
function setDefaultVars (line 70) | func setDefaultVars(vars map[string]string) {
function ReadConfig (line 82) | func ReadConfig(project string, opts ...option.ClientOption) (map[string...
function GetBoolean (line 106) | func GetBoolean(vars map[string]string, key string, def bool) bool {
FILE: src/go/pkg/configutil/config_reader_test.go
function TestBashUnescape (line 23) | func TestBashUnescape(t *testing.T) {
function TestGetConfigFromReader (line 53) | func TestGetConfigFromReader(t *testing.T) {
function TestSetDefaultVars (line 77) | func TestSetDefaultVars(t *testing.T) {
function TestGetBoolean (line 111) | func TestGetBoolean(t *testing.T) {
FILE: src/go/pkg/controller/approllout/controller.go
constant fieldIndexOwners (line 54) | fieldIndexOwners = "metadata.ownerReferences.uid"
constant fieldIndexAppName (line 55) | fieldIndexAppName = "spec.appName"
constant labelRobotName (line 56) | labelRobotName = "cloudrobotics.com/robot-name"
function Add (line 61) | func Add(ctx context.Context, mgr manager.Manager, baseValues chartutil....
type Reconciler (line 209) | type Reconciler struct
method enqueueForApp (line 166) | func (r *Reconciler) enqueueForApp(ctx context.Context, m metav1.Objec...
method enqueueForOwner (line 182) | func (r *Reconciler) enqueueForOwner(m metav1.Object, q workqueue.Rate...
method enqueueAll (line 193) | func (r *Reconciler) enqueueAll(ctx context.Context, q workqueue.RateL...
method Reconcile (line 214) | func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Requ...
method reconcile (line 227) | func (r *Reconciler) reconcile(ctx context.Context, ar *apps.AppRollou...
method updateErrorStatus (line 357) | func (r *Reconciler) updateErrorStatus(ctx context.Context, ar *apps.A...
function setStatus (line 365) | func setStatus(ar *apps.AppRollout, numWantCAs int, curCAs []apps.ChartA...
function setCondition (line 395) | func setCondition(ar *apps.AppRollout, t apps.AppRolloutConditionType, s...
function chartAssignmentChanged (line 425) | func chartAssignmentChanged(prev, cur *apps.ChartAssignment) (bool, erro...
type errRobotSelectorOverlap (line 443) | type errRobotSelectorOverlap
method Error (line 445) | func (r errRobotSelectorOverlap) Error() string {
function generateChartAssignments (line 451) | func generateChartAssignments(
function newCloudChartAssignment (line 542) | func newCloudChartAssignment(
function newRobotChartAssignment (line 572) | func newRobotChartAssignment(
function newBaseChartAssignment (line 601) | func newBaseChartAssignment(app *apps.App, rollout *apps.AppRollout, com...
function matchingRobots (line 630) | func matchingRobots(robots []registry.Robot, sel *apps.RobotSelector) ([...
function appNamespaceName (line 650) | func appNamespaceName(rollout string) string {
type componentType (line 654) | type componentType
constant compTypeRobot (line 657) | compTypeRobot componentType = "robot"
constant compTypeCloud (line 658) | compTypeCloud = "cloud"
function chartAssignmentName (line 661) | func chartAssignmentName(rollout string, typ componentType, robot string...
type robotValues (line 670) | type robotValues struct
function setLabel (line 674) | func setLabel(o *metav1.ObjectMeta, k, v string) {
function setAnnotation (line 681) | func setAnnotation(o *metav1.ObjectMeta, k, v string) {
function setOwnerReference (line 690) | func setOwnerReference(om *metav1.ObjectMeta, ref metav1.OwnerReference) {
function indexOwnerReferences (line 701) | func indexOwnerReferences(o kclient.Object) (vs []string) {
function indexAppName (line 709) | func indexAppName(o kclient.Object) []string {
constant labelAppName (line 716) | labelAppName = "cloudrobotics.com/app-name"
constant labelAppVersion (line 719) | labelAppVersion = "cloudrobotics.com/app-version"
function NewAppValidationWebhook (line 736) | func NewAppValidationWebhook(mgr manager.Manager) *admission.Webhook {
type appValidator (line 741) | type appValidator struct
method Handle (line 751) | func (v *appValidator) Handle(_ context.Context, req admission.Request...
function newAppValidator (line 745) | func newAppValidator(sc *runtime.Scheme) *appValidator {
function appValidate (line 762) | func appValidate(cur *apps.App) error {
function NewAppRolloutValidationWebhook (line 785) | func NewAppRolloutValidationWebhook(mgr manager.Manager) *admission.Webh...
type appRolloutValidator (line 790) | type appRolloutValidator struct
method Handle (line 800) | func (v *appRolloutValidator) Handle(_ context.Context, req admission....
function newAppRolloutValidator (line 794) | func newAppRolloutValidator(sc *runtime.Scheme) *appRolloutValidator {
function appRolloutValidate (line 812) | func appRolloutValidate(cur *apps.AppRollout) error {
FILE: src/go/pkg/controller/approllout/controller_test.go
function marshalYAML (line 29) | func marshalYAML(t *testing.T, v interface{}) string {
function unmarshalYAML (line 38) | func unmarshalYAML(t *testing.T, v interface{}, s string) {
function verifyChartAssignment (line 45) | func verifyChartAssignment(t *testing.T, want, got *apps.ChartAssignment) {
function TestNewRobotChartAssignment (line 57) | func TestNewRobotChartAssignment(t *testing.T) {
function TestNewCloudChartAssignment (line 128) | func TestNewCloudChartAssignment(t *testing.T) {
function TestGenerateChartAssignments (line 201) | func TestGenerateChartAssignments(t *testing.T) {
function generateApp (line 325) | func generateApp(name, version, robotPayload, cloudPayload string) apps....
function generateRobot (line 350) | func generateRobot(name string, labels map[string]string) registry.Robot {
function generateRollout (line 360) | func generateRollout(name, appName string) apps.AppRollout {
function addRobotToRollout (line 372) | func addRobotToRollout(ar *apps.AppRollout, matchLabel, matchValue, vers...
function TestGenerateChartAssignments_vApps (line 388) | func TestGenerateChartAssignments_vApps(t *testing.T) {
function TestGenerateChartAssignments_vAppMissing (line 422) | func TestGenerateChartAssignments_vAppMissing(t *testing.T) {
function TestGenerateChartAssignments_vAppCloud (line 447) | func TestGenerateChartAssignments_vAppCloud(t *testing.T) {
function TestGenerateChartAssignments_vAppCloudMissing (line 483) | func TestGenerateChartAssignments_vAppCloudMissing(t *testing.T) {
function TestGenerateChartAssignments_cloudPerRobot (line 508) | func TestGenerateChartAssignments_cloudPerRobot(t *testing.T) {
function TestGenerateChartAssignments_selectorOverlap (line 575) | func TestGenerateChartAssignments_selectorOverlap(t *testing.T) {
function TestSetStatus (line 619) | func TestSetStatus(t *testing.T) {
function TestValidateAppRollout (line 665) | func TestValidateAppRollout(t *testing.T) {
function TestValidateApp (line 780) | func TestValidateApp(t *testing.T) {
FILE: src/go/pkg/controller/chartassignment/controller.go
constant defaultServiceAccountDeadline (line 45) | defaultServiceAccountDeadline = time.Minute
constant fieldIndexNamespace (line 46) | fieldIndexNamespace = "spec.namespaceName"
constant statusCheckingOptOutLabel (line 47) | statusCheckingOptOutLabel = "cloudrobotics.com/opt-out-error-checking"
function Add (line 53) | func Add(ctx context.Context, mgr manager.Manager, cloud bool) error {
type Reconciler (line 122) | type Reconciler struct
method enqueueForPod (line 106) | func (r *Reconciler) enqueueForPod(ctx context.Context, m meta.Object,...
method Reconcile (line 133) | func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Requ...
method ensureNamespace (line 187) | func (r *Reconciler) ensureNamespace(ctx context.Context, as *apps.Cha...
method ensureSecrets (line 225) | func (r *Reconciler) ensureSecrets(ctx context.Context, as *apps.Chart...
method ensureServiceAccount (line 253) | func (r *Reconciler) ensureServiceAccount(ctx context.Context, ns *cor...
method reconcile (line 294) | func (r *Reconciler) reconcile(ctx context.Context, as *apps.ChartAssi...
method setStatus (line 366) | func (r *Reconciler) setStatus(ctx context.Context, as *apps.ChartAssi...
method ensureDeleted (line 433) | func (r *Reconciler) ensureDeleted(ctx context.Context, as *apps.Chart...
constant finalizer (line 160) | finalizer = "helm.apps.cloudrobotics.com"
constant requeueFast (line 162) | requeueFast = 3 * time.Second
constant requeueSlow (line 164) | requeueSlow = 3 * time.Minute
type namespaceDeletionError (line 172) | type namespaceDeletionError struct
method Error (line 176) | func (e *namespaceDeletionError) Error() string { return e.msg }
type missingServiceAccountError (line 181) | type missingServiceAccountError struct
method Error (line 185) | func (e *missingServiceAccountError) Error() string { return e.msg }
function condition (line 359) | func condition(b bool) core.ConditionStatus {
function stringsContain (line 454) | func stringsContain(list []string, s string) bool {
function stringsDelete (line 463) | func stringsDelete(list []string, s string) (res []string) {
function setOwnerReference (line 474) | func setOwnerReference(om *meta.ObjectMeta, ref meta.OwnerReference) bool {
function inCondition (line 487) | func inCondition(as *apps.ChartAssignment, c apps.ChartAssignmentConditi...
function setCondition (line 498) | func setCondition(as *apps.ChartAssignment, t apps.ChartAssignmentCondit...
FILE: src/go/pkg/controller/chartassignment/release.go
type releases (line 50) | type releases struct
method status (line 90) | func (rs *releases) status(name string) (releaseStatus, bool) {
method add (line 103) | func (rs *releases) add(name string) *release {
method ensureUpdated (line 127) | func (rs *releases) ensureUpdated(as *apps.ChartAssignment) bool {
method ensureDeleted (line 152) | func (rs *releases) ensureDeleted(as *apps.ChartAssignment) bool {
function newReleases (line 58) | func newReleases(cfg *rest.Config, rec record.EventRecorder) (*releases,...
type release (line 71) | type release struct
method run (line 159) | func (r *release) run() {
method start (line 167) | func (r *release) start(f func()) bool {
method setPhase (line 176) | func (r *release) setPhase(p apps.ChartAssignmentPhase) {
method generation (line 184) | func (r *release) generation() int64 {
method setGeneration (line 190) | func (r *release) setGeneration(generation int64) {
method setFailed (line 196) | func (r *release) setFailed(err error, retry bool) {
method delete (line 213) | func (r *release) delete(as *apps.ChartAssignment) {
method update (line 235) | func (r *release) update(as *apps.ChartAssignment) {
type releaseStatus (line 82) | type releaseStatus struct
function loadAndExpandChart (line 281) | func loadAndExpandChart(as *apps.ChartAssignment) ([]*unstructured.Unstr...
function loadChart (line 307) | func loadChart(cspec *apps.AssignedChart) (*chart.Chart, string, error) {
function fetchChartTar (line 350) | func fetchChartTar(repoURL, name, version string) (io.Reader, error) {
function newHTTPGetter (line 391) | func newHTTPGetter(url, certFile, keyFile, caFile string) (getter.Getter...
function decodeManifests (line 395) | func decodeManifests(manifests map[string]string) ([]*unstructured.Unstr...
FILE: src/go/pkg/controller/chartassignment/release_test.go
constant ChartName (line 14) | ChartName = "testchart"
function verifyValues (line 17) | func verifyValues(t *testing.T, have string, wantValues chartutil.Values) {
function Test_loadChart_mergesValues (line 25) | func Test_loadChart_mergesValues(t *testing.T) {
function Test_loadChartWithoutTemplates_returnsZeroManifests (line 54) | func Test_loadChartWithoutTemplates_returnsZeroManifests(t *testing.T) {
function Test_updateSynk_callsApply (line 74) | func Test_updateSynk_callsApply(t *testing.T) {
function Test_deleteSynk_callsDelete (line 101) | func Test_deleteSynk_callsDelete(t *testing.T) {
FILE: src/go/pkg/controller/chartassignment/validator.go
function NewValidationWebhook (line 33) | func NewValidationWebhook(mgr manager.Manager) *admission.Webhook {
type chartAssignmentValidator (line 38) | type chartAssignmentValidator struct
method Handle (line 48) | func (v *chartAssignmentValidator) Handle(_ context.Context, req admis...
method validate (line 68) | func (v *chartAssignmentValidator) validate(cur, old *apps.ChartAssign...
function newChartAssignmentValidator (line 42) | func newChartAssignmentValidator(sc *runtime.Scheme) *chartAssignmentVal...
FILE: src/go/pkg/controller/chartassignment/validator_test.go
function unmarshalYAML (line 25) | func unmarshalYAML(t *testing.T, v interface{}, s string) {
function TestValidate (line 32) | func TestValidate(t *testing.T) {
FILE: src/go/pkg/gcr/update_gcr_credential_test.go
function TestDockercfgJSON (line 23) | func TestDockercfgJSON(t *testing.T) {
FILE: src/go/pkg/gcr/update_gcr_credentials.go
constant SecretName (line 40) | SecretName = "gcr-json-key"
function DockerCfgJSON (line 44) | func DockerCfgJSON(token string) []byte {
function patchServiceAccount (line 69) | func patchServiceAccount(ctx context.Context, k8s *kubernetes.Clientset,...
function UpdateGcrCredentials (line 91) | func UpdateGcrCredentials(ctx context.Context, k8s *kubernetes.Clientset...
FILE: src/go/pkg/kubetest/kubetest.go
type Environment (line 59) | type Environment struct
method Ctx (line 138) | func (e *Environment) Ctx() context.Context {
method InstallChartArchive (line 152) | func (e *Environment) InstallChartArchive(cluster, name, namespace, pa...
method Client (line 186) | func (e *Environment) Client(cluster string) client.Client {
method Teardown (line 201) | func (e *Environment) Teardown() {
method Run (line 225) | func (e *Environment) Run(tests ...TestFunc) {
method Uniq (line 243) | func (e *Environment) Uniq(s string) string {
method New (line 257) | func (env *Environment) New(testFn TestFunc) *Fixture {
type Config (line 69) | type Config struct
type ClusterConfig (line 77) | type ClusterConfig struct
type cluster (line 81) | type cluster struct
function New (line 90) | func New(t *testing.T, cfg Config) *Environment {
function helmValues (line 142) | func helmValues(vars map[string]string) string {
type TestFunc (line 222) | type TestFunc
type Fixture (line 249) | type Fixture struct
method Ctx (line 265) | func (f *Fixture) Ctx() context.Context {
method ObjectKey (line 270) | func (f *Fixture) ObjectKey(o client.Object) client.ObjectKey {
method Uniq (line 277) | func (f *Fixture) Uniq(s string) string {
method FromYAML (line 284) | func (f *Fixture) FromYAML(tmpl string, vals, dst interface{}) {
method Client (line 336) | func (f *Fixture) Client(cluster string) client.Client {
method ChartAssignmentHasStatus (line 507) | func (f *Fixture) ChartAssignmentHasStatus(ca *crcapps.ChartAssignment...
function BuildInlineChart (line 302) | func BuildInlineChart(t *testing.T, name, tmpl, values string) string {
function addFileToTar (line 322) | func addFileToTar(tw *tar.Writer, path, content string) error {
function setupCluster (line 342) | func setupCluster(synkPath string, cluster *cluster) error {
function DeploymentReady (line 488) | func DeploymentReady(ctx context.Context, c client.Client, namespace, na...
FILE: src/go/pkg/kubeutils/kubeutils.go
constant LocalContext (line 38) | LocalContext = "kubernetes-admin@kubernetes"
constant localConfig (line 40) | localConfig = "~/.kube/config"
constant deletionTimeoutSeconds (line 41) | deletionTimeoutSeconds = 60
function ExpandUser (line 45) | func ExpandUser(path string) string {
function CloudKubernetesContextName (line 55) | func CloudKubernetesContextName(projectID, region string) string {
function GetCloudKubernetesContext (line 60) | func GetCloudKubernetesContext() (string, error) {
function GetRobotKubernetesContext (line 75) | func GetRobotKubernetesContext() (string, error) {
function LoadOutOfClusterConfigLocal (line 85) | func LoadOutOfClusterConfigLocal() (*rest.Config, error) {
function LoadOutOfClusterConfig (line 89) | func LoadOutOfClusterConfig(context string) (*rest.Config, error) {
type PrefixingRoundtripper (line 102) | type PrefixingRoundtripper struct
method RoundTrip (line 107) | func (pr *PrefixingRoundtripper) RoundTrip(r *http.Request) (*http.Res...
function BuildCloudKubernetesConfig (line 119) | func BuildCloudKubernetesConfig(ts oauth2.TokenSource, remoteServer stri...
function UpdateSecret (line 134) | func UpdateSecret(ctx context.Context, k8s kubernetes.Interface, input *...
FILE: src/go/pkg/robotauth/robotauth.go
constant credentialsFile (line 49) | credentialsFile = "~/.config/cloud-robotics/robot-id.json"
type RobotAuth (line 53) | type RobotAuth struct
method StoreInFile (line 103) | func (r *RobotAuth) StoreInFile() error {
method StoreInK8sSecret (line 123) | func (r *RobotAuth) StoreInK8sSecret(ctx context.Context, clientset ku...
method CreatePrivateKey (line 143) | func (r *RobotAuth) CreatePrivateKey() error {
method getTokenEndpoint (line 159) | func (r *RobotAuth) getTokenEndpoint() string {
method CreateRobotTokenSource (line 166) | func (r *RobotAuth) CreateRobotTokenSource(ctx context.Context, gcpSaC...
method CreateJWT (line 186) | func (r *RobotAuth) CreateJWT(ctx context.Context, lifetime time.Durat...
method ServiceAccountEmail (line 232) | func (r *RobotAuth) ServiceAccountEmail(saName string) (string, error) {
function filename (line 61) | func filename() string {
function LoadFromFile (line 67) | func LoadFromFile(keyfile string) (*RobotAuth, error) {
function LoadFromK8sSecret (line 85) | func LoadFromK8sSecret(ctx context.Context, clientset kubernetes.Interfa...
type robotJWTSource (line 249) | type robotJWTSource struct
method Token (line 256) | func (ts *robotJWTSource) Token() (*oauth2.Token, error) {
constant jwtMinLifetime (line 293) | jwtMinLifetime = time.Minute
function CreateJWTSource (line 298) | func CreateJWTSource() oauth2.TokenSource {
FILE: src/go/pkg/robotauth/robotauth_test.go
constant testPubKey (line 20) | testPubKey = `
constant testPrivKey (line 31) | testPrivKey = `
function TestK8sSecretLoadStoreRoundtrip (line 63) | func TestK8sSecretLoadStoreRoundtrip(t *testing.T) {
function TestCreateJWT (line 86) | func TestCreateJWT(t *testing.T) {
type mockRoundTripper (line 114) | type mockRoundTripper struct
method RoundTrip (line 118) | func (rt *mockRoundTripper) RoundTrip(req *http.Request) (*http.Respon...
function makeTokenResponse (line 122) | func makeTokenResponse(token string) *http.Response {
constant testToken (line 133) | testToken = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJyb2JvdC1yb2...
constant textTokenExpiryUnix (line 135) | textTokenExpiryUnix = 1741615721
function TestCreateJWTSource (line 138) | func TestCreateJWTSource(t *testing.T) {
FILE: src/go/pkg/setup/setupcommon.go
function GetRobotName (line 45) | func GetRobotName(ctx context.Context, f util.Factory, client dynamic.Re...
function exitIfNotRunningInTerminal (line 72) | func exitIfNotRunningInTerminal(message ...interface{}) {
function selectRobot (line 81) | func selectRobot(f util.Factory, robots []unstructured.Unstructured) (st...
function newExponentialBackoff (line 105) | func newExponentialBackoff(initialInterval time.Duration, multiplier flo...
function WaitForDNS (line 118) | func WaitForDNS(domain string, retries uint64) error {
function WaitForService (line 152) | func WaitForService(client *http.Client, url string, retries uint64) err...
function PublishCredentialsToCloud (line 173) | func PublishCredentialsToCloud(client *http.Client, auth *robotauth.Robo...
function isKeyRegistryAvailable (line 186) | func isKeyRegistryAvailable(auth *robotauth.RobotAuth, client *http.Clie...
function publishPublicKeyToCloudRegistry (line 195) | func publishPublicKeyToCloudRegistry(auth *robotauth.RobotAuth, client *...
function getPublicKey (line 226) | func getPublicKey(privateKey []byte) ([]byte, error) {
function mergeMaps (line 259) | func mergeMaps(base, additions map[string]string) map[string]string {
function CreateOrUpdateRobot (line 271) | func CreateOrUpdateRobot(ctx context.Context, client dynamic.ResourceInt...
FILE: src/go/pkg/setup/setupcommon_test.go
function TestSelectRobot (line 26) | func TestSelectRobot(t *testing.T) {
function TestWaitForService_OkIfServiceResponds (line 59) | func TestWaitForService_OkIfServiceResponds(t *testing.T) {
FILE: src/go/pkg/setup/util/factory.go
type Factory (line 24) | type Factory interface
type DefaultFactory (line 29) | type DefaultFactory struct
method ScanInt (line 36) | func (f *DefaultFactory) ScanInt() (int, error) {
method GetNetworkInterfaceIP (line 52) | func (f *DefaultFactory) GetNetworkInterfaceIP(namePrefix string) (str...
function NewFactory (line 31) | func NewFactory() *DefaultFactory {
FILE: src/go/pkg/setup/util/fake.go
type TestFactory (line 17) | type TestFactory struct
method ScanInt (line 25) | func (f *TestFactory) ScanInt() (int, error) {
function NewTestFactory (line 21) | func NewTestFactory() *TestFactory {
FILE: src/go/pkg/synk/interface.go
type Interface (line 10) | type Interface interface
FILE: src/go/pkg/synk/sort.go
type gvknn (line 25) | type gvknn struct
function newGvknn (line 34) | func newGvknn(group, version, kind, namespace, name string) *gvknn {
function less (line 51) | func less(l, r *gvknn) bool {
function gvknnUnstructured (line 57) | func gvknnUnstructured(u *unstructured.Unstructured) *gvknn {
function gvknnRSpecG (line 62) | func gvknnRSpecG(r *apps.ResourceSetSpecGroup) *gvknn {
function gvknnRStatusG (line 66) | func gvknnRStatusG(r *apps.ResourceSetStatusGroup) *gvknn {
function lessUnstructured (line 70) | func lessUnstructured(l, r *unstructured.Unstructured) bool {
function lessResourceSetSpecGroup (line 74) | func lessResourceSetSpecGroup(l, r *apps.ResourceSetSpecGroup) bool {
function lessResourceSetStatusGroup (line 78) | func lessResourceSetStatusGroup(l, r *apps.ResourceSetStatusGroup) bool {
FILE: src/go/pkg/synk/sort_test.go
function TestLessResourceSetStatusGroup (line 23) | func TestLessResourceSetStatusGroup(t *testing.T) {
function TestLessResourceSetSpecGroup (line 42) | func TestLessResourceSetSpecGroup(t *testing.T) {
function TestLessUnstructured (line 61) | func TestLessUnstructured(t *testing.T) {
function TestLess (line 72) | func TestLess(t *testing.T) {
FILE: src/go/pkg/synk/synk.go
constant totalAnnotationSizeLimitB (line 57) | totalAnnotationSizeLimitB int = 256 * (1 << 10)
type Synk (line 60) | type Synk struct
method Init (line 134) | func (s *Synk) Init() error {
method Delete (line 215) | func (s *Synk) Delete(ctx context.Context, name string) error {
method Apply (line 224) | func (s *Synk) Apply(
method applyAll (line 309) | func (s *Synk) applyAll(
method initialize (line 415) | func (s *Synk) initialize(
method populateNamespaces (line 484) | func (s *Synk) populateNamespaces(
method applyOne (line 670) | func (s *Synk) applyOne(ctx context.Context, resource *unstructured.Un...
method crdAvailable (line 826) | func (s *Synk) crdAvailable(ucrd *unstructured.Unstructured) (bool, er...
method createResourceSet (line 871) | func (s *Synk) createResourceSet(ctx context.Context, rs *apps.Resourc...
method updateResourceSetStatus (line 923) | func (s *Synk) updateResourceSetStatus(ctx context.Context, rs *apps.R...
method deleteFailedResourceSets (line 982) | func (s *Synk) deleteFailedResourceSets(ctx context.Context, name stri...
method deleteResourceSets (line 1016) | func (s *Synk) deleteResourceSets(ctx context.Context, name string, ve...
method next (line 1042) | func (s *Synk) next(ctx context.Context, name string) (version int32, ...
function New (line 68) | func New(client dynamic.Interface, discovery discovery.CachedDiscoveryIn...
function NewForConfig (line 81) | func NewForConfig(cfg *rest.Config) (*Synk, error) {
type ApplyOptions (line 99) | type ApplyOptions struct
method logf (line 119) | func (o *ApplyOptions) logf(r *unstructured.Unstructured, action apps....
method errorf (line 125) | func (o *ApplyOptions) errorf(r *unstructured.Unstructured, action app...
constant StatusSuccess (line 115) | StatusSuccess = "success"
constant StatusFailure (line 116) | StatusFailure = "failure"
type transientErr (line 267) | type transientErr struct
function IsTransientErr (line 272) | func IsTransientErr(err error) bool {
function deleteAppliedAnnotation (line 544) | func deleteAppliedAnnotation(u *unstructured.Unstructured) {
function setAppliedAnnotation (line 554) | func setAppliedAnnotation(u *unstructured.Unstructured) error {
function getAppliedAnnotation (line 571) | func getAppliedAnnotation(u *unstructured.Unstructured) []byte {
function validateOwnerRefs (line 577) | func validateOwnerRefs(r *unstructured.Unstructured, set *apps.ResourceS...
function setOwnerRef (line 606) | func setOwnerRef(r *unstructured.Unstructured, set *apps.ResourceSet) {
function canReplace (line 628) | func canReplace(resource *unstructured.Unstructured, patchErr error) bool {
function replace (line 652) | func replace(ctx context.Context, client dynamic.ResourceInterface, reso...
type applyResult (line 886) | type applyResult struct
method String (line 892) | func (r *applyResult) String() string {
type applyResults (line 896) | type applyResults
method set (line 898) | func (r applyResults) set(res *unstructured.Unstructured, action apps....
method failed (line 906) | func (r applyResults) failed(res *unstructured.Unstructured) bool {
method list (line 913) | func (r applyResults) list() (l []*applyResult) {
function isTestResource (line 1062) | func isTestResource(r *unstructured.Unstructured) bool {
function isCustomResourceDefinition (line 1067) | func isCustomResourceDefinition(r *unstructured.Unstructured) bool {
function separateCRDsFromResources (line 1071) | func separateCRDsFromResources(resources []*unstructured.Unstructured) (...
function filter (line 1082) | func filter(in []*unstructured.Unstructured, f func(*unstructured.Unstru...
function resourceSetName (line 1091) | func resourceSetName(s string, v int32) string {
function decodeResourceSetName (line 1097) | func decodeResourceSetName(s string) (string, int32, bool) {
function sortResources (line 1109) | func sortResources(res []*unstructured.Unstructured) {
function resourceKey (line 1115) | func resourceKey(r *unstructured.Unstructured) string {
function gvkKey (line 1123) | func gvkKey(group, version, kind string) string {
function convert (line 1128) | func convert(from, to runtime.Object) error {
FILE: src/go/pkg/synk/synk_test.go
type fakeCachedDiscoveryClient (line 44) | type fakeCachedDiscoveryClient struct
method Invalidate (line 48) | func (d *fakeCachedDiscoveryClient) Invalidate() {}
method ServerGroupsAndResources (line 50) | func (d *fakeCachedDiscoveryClient) ServerGroupsAndResources() ([]*met...
type fixture (line 62) | type fixture struct
method newSynk (line 76) | func (f *fixture) newSynk() *Synk {
method addObjects (line 90) | func (f *fixture) addObjects(objs ...runtime.Object) {
method expectActions (line 94) | func (f *fixture) expectActions(as ...k8stest.Action) {
method verifyWriteActions (line 98) | func (f *fixture) verifyWriteActions() {
function newFixture (line 72) | func newFixture(t *testing.T) *fixture {
function TestSynk_IsTransientErr (line 114) | func TestSynk_IsTransientErr(t *testing.T) {
function TestSynk_initialize (line 158) | func TestSynk_initialize(t *testing.T) {
function TestSynk_updateResourceSetStatus (line 213) | func TestSynk_updateResourceSetStatus(t *testing.T) {
function TestSynk_applyAllIsUpdatingResources (line 306) | func TestSynk_applyAllIsUpdatingResources(t *testing.T) {
function TestSynk_applyAllIsCreatingResources (line 370) | func TestSynk_applyAllIsCreatingResources(t *testing.T) {
function TestSynk_applyAllRetriesResourceExpired (line 416) | func TestSynk_applyAllRetriesResourceExpired(t *testing.T) {
function TestSynk_skipsTestResources (line 491) | func TestSynk_skipsTestResources(t *testing.T) {
function TestSynk_deleteResourceSets (line 532) | func TestSynk_deleteResourceSets(t *testing.T) {
function TestSynk_deleteFailedResourceSets (line 561) | func TestSynk_deleteFailedResourceSets(t *testing.T) {
function TestSynk_populateNamespaces (line 592) | func TestSynk_populateNamespaces(t *testing.T) {
function TestSynk_skipLastAppliedAnnotationForLargeResource (line 641) | func TestSynk_skipLastAppliedAnnotationForLargeResource(t *testing.T) {
function newUnstructured (line 662) | func newUnstructured(apiVersion, kind, namespace, name string) *unstruct...
function unmarshalYAML (line 671) | func unmarshalYAML(t *testing.T, v interface{}, s string) {
function toUnstructured (line 678) | func toUnstructured(t *testing.T, o runtime.Object) *unstructured.Unstru...
function filterReadActions (line 688) | func filterReadActions(actions []k8stest.Action) (ret []k8stest.Action) {
function sprintAction (line 698) | func sprintAction(a k8stest.Action) string {
FILE: src/go/tests/apps/apps_test.go
constant robotClusterName (line 33) | robotClusterName = "robot"
constant inlineChartTemplate (line 35) | inlineChartTemplate = `
constant goodDeployment (line 46) | goodDeployment = `
constant deploymentWithBadLabels (line 65) | deploymentWithBadLabels = `
constant badJob (line 84) | badJob = `
constant goodJob (line 100) | goodJob = `
function TestAll (line 118) | func TestAll(t *testing.T) {
function testCreateChartAssignment_WithChartReference_Works (line 157) | func testCreateChartAssignment_WithChartReference_Works(t *testing.T, f ...
function testCreateChartAssignment_WithInlineChart_BecomesReady (line 214) | func testCreateChartAssignment_WithInlineChart_BecomesReady(t *testing.T...
function testCreateChartAssignment_WithBadDeployment_BecomesFailed (line 238) | func testCreateChartAssignment_WithBadDeployment_BecomesFailed(t *testin...
function testUpdateChartAssignment_WithFixedDeployment_BecomesReady (line 262) | func testUpdateChartAssignment_WithFixedDeployment_BecomesReady(t *testi...
function testUpdateChartAssignment_WithFixedJob_BecomesReady (line 313) | func testUpdateChartAssignment_WithFixedJob_BecomesReady(t *testing.T, f...
function testCreateChartAssignment_CopiesLabelledSecret (line 364) | func testCreateChartAssignment_CopiesLabelledSecret(t *testing.T, f *kub...
FILE: src/go/tests/k8s_integration_test.go
constant appInitializationTimeout (line 39) | appInitializationTimeout = 7 * time.Minute
constant podInitializationTimeout (line 40) | podInitializationTimeout = 5 * time.Minute
function checkHealthOfKubernetesCluster (line 43) | func checkHealthOfKubernetesCluster(ctx context.Context, kubernetesConte...
function convert (line 137) | func convert(from, to runtime.Object) error {
function TestCloudClusterAppStatus (line 145) | func TestCloudClusterAppStatus(t *testing.T) {
function TestKubernetesCloudClusterStatus (line 210) | func TestKubernetesCloudClusterStatus(t *testing.T) {
function TestKubernetesRobotClusterStatus (line 222) | func TestKubernetesRobotClusterStatus(t *testing.T) {
FILE: src/go/tests/k8s_integration_test_auth_helper.go
function init (line 47) | func init() {
type gcpAuthProvider (line 118) | type gcpAuthProvider struct
method WrapTransport (line 190) | func (g *gcpAuthProvider) WrapTransport(rt http.RoundTripper) http.Rou...
method Login (line 200) | func (g *gcpAuthProvider) Login() error { return nil }
function newGCPAuthProvider (line 125) | func newGCPAuthProvider(_ string, gcpConfig map[string]string, persister...
function isCmdTokenSource (line 142) | func isCmdTokenSource(gcpConfig map[string]string) bool {
function tokenSource (line 147) | func tokenSource(isCmd bool, gcpConfig map[string]string) (oauth2.TokenS...
function parseScopes (line 179) | func parseScopes(gcpConfig map[string]string) []string {
type cachedTokenSource (line 202) | type cachedTokenSource struct
method Token (line 228) | func (t *cachedTokenSource) Token() (*oauth2.Token, error) {
method cachedToken (line 246) | func (t *cachedTokenSource) cachedToken() *oauth2.Token {
method update (line 256) | func (t *cachedTokenSource) update(tok *oauth2.Token) map[string]string {
method baseCache (line 271) | func (t *cachedTokenSource) baseCache() map[string]string {
function newCachedTokenSource (line 211) | func newCachedTokenSource(accessToken, expiry string, persister restclie...
type commandTokenSource (line 283) | type commandTokenSource struct
method Token (line 310) | func (c *commandTokenSource) Token() (*oauth2.Token, error) {
method parseTokenCmdOutput (line 326) | func (c *commandTokenSource) parseTokenCmdOutput(output []byte) (*oaut...
function newCmdTokenSource (line 291) | func newCmdTokenSource(cmd string, args []string, tokenKey, expiryKey, t...
function parseJSONPath (line 358) | func parseJSONPath(input interface{}, name, template string) (string, er...
type conditionalTransport (line 370) | type conditionalTransport struct
method RoundTrip (line 378) | func (t *conditionalTransport) RoundTrip(req *http.Request) (*http.Res...
method WrappedRoundTripper (line 397) | func (t *conditionalTransport) WrappedRoundTripper() http.RoundTripper...
FILE: src/go/tests/relay/in_process_relay_test.go
function pickUnusedPortOrDie (line 27) | func pickUnusedPortOrDie() int {
function initRelay (line 41) | func initRelay() {
function serveFunction (line 91) | func serveFunction(
function serveFunctionWithTimeout (line 96) | func serveFunctionWithTimeout(
function TestHttpResponse (line 115) | func TestHttpResponse(t *testing.T) {
function TestHttpTimeout (line 139) | func TestHttpTimeout(t *testing.T) {
function TestHttpErrorPropagation (line 161) | func TestHttpErrorPropagation(t *testing.T) {
FILE: src/go/tests/relay/nok8s_relay_test.go
constant RelayClientPath (line 45) | RelayClientPath = "src/go/cmd/http-relay-client/http-relay-client-app_/h...
constant RelayServerPath (line 46) | RelayServerPath = "src/go/cmd/http-relay-server/http-relay-server-app_/h...
type relay (line 62) | type relay struct
method start (line 68) | func (r *relay) start(backendAddress string, extraClientArgs ...string...
method stop (line 123) | func (r *relay) stop() error {
function TestHttpRelay (line 136) | func TestHttpRelay(t *testing.T) {
function TestDroppedUserClientFreesRelayChannel (line 209) | func TestDroppedUserClientFreesRelayChannel(t *testing.T) {
function TestDroppedBidiStreamFreesRelayChannel (line 266) | func TestDroppedBidiStreamFreesRelayChannel(t *testing.T) {
type testServer (line 329) | type testServer struct
method EmptyCall (line 334) | func (s *testServer) EmptyCall(ctx context.Context, in *testpb.Empty) ...
method UnaryCall (line 338) | func (s *testServer) UnaryCall(ctx context.Context, in *testpb.SimpleR...
type relayWithGrpcServer (line 346) | type relayWithGrpcServer struct
method mustStop (line 354) | func (r *relayWithGrpcServer) mustStop(t *testing.T) {
function mustStartRelayWithGrpcServer (line 363) | func mustStartRelayWithGrpcServer(t *testing.T, service testpb.TestServi...
function TestGrpcRelaySimpleCallWorks (line 398) | func TestGrpcRelaySimpleCallWorks(t *testing.T) {
function TestGrpcRelayChunkingOfLargeResponseWorks (line 414) | func TestGrpcRelayChunkingOfLargeResponseWorks(t *testing.T) {
function TestGrpcRelayErrorArePropagated (line 440) | func TestGrpcRelayErrorArePropagated(t *testing.T) {
Condensed preview — 462 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (3,878K chars).
[
{
"path": ".bazelignore",
"chars": 12,
"preview": "src/.gopath\n"
},
{
"path": ".bazelrc",
"chars": 2101,
"preview": "# Enable Bzlmod for every Bazel command\ncommon --enable_bzlmod\n\n# Work around go issue with LLVM 15+: https://github.com"
},
{
"path": ".bazelversion",
"chars": 6,
"preview": "8.4.2\n"
},
{
"path": ".dockerignore",
"chars": 2,
"preview": "*\n"
},
{
"path": ".editorconfig",
"chars": 288,
"preview": "# Editor configuration, see http://editorconfig.org\nroot = true\n\n[*]\ncharset = utf-8\nindent_style = space\nindent_size = "
},
{
"path": ".github/ci/.bazelrc",
"chars": 950,
"preview": "# Bazel config for CI/CD builds.\n\n# Default to keep going\nbuild --keep_going\n\n# Use rbe remote execution and caching on "
},
{
"path": ".github/ci/Dockerfile.integration-test-image",
"chars": 1421,
"preview": "# Image used for integration_test.sh on Cloud Build.\n# Allows access to GKE and to run Bazel commands.\nFROM gcr.io/cloud"
},
{
"path": ".github/ci/common.sh",
"chars": 3027,
"preview": "#!/bin/bash\n\n# Format for the xtrace lines\nexport 'PS4=+$(date --rfc-3339=seconds):${BASH_SOURCE}:${LINENO}: '\nset -o er"
},
{
"path": ".github/ci/deploy_navtest.sh",
"chars": 601,
"preview": "#!/bin/bash\n\nDIR=\"$( cd \"$( dirname \"${BASH_SOURCE[0]}\" )\" && pwd )\"\nsource \"${DIR}/common.sh\"\n\nPROJECT_DIR=\"${DIR}/depl"
},
{
"path": ".github/ci/deploy_navtest_cloudbuild.yaml",
"chars": 249,
"preview": "# Call deploy_navtest.sh on Cloud Build.\n# TODO(b/323509860): Run directly on the Action runner when it supports WIF.\nst"
},
{
"path": ".github/ci/deployments/robco-integration-test/config.sh",
"chars": 550,
"preview": "#!/usr/bin/env bash\n\n# Enable cloud robotics layer 2\nAPP_MANAGEMENT=true\n\nGCP_PROJECT_ID=robco-integration-test\nGCP_REGI"
},
{
"path": ".github/ci/deployments/robco-integration-test/kubernetes/k8s-relay-rollout.yaml",
"chars": 201,
"preview": "apiVersion: apps.cloudrobotics.com/v1alpha1\nkind: AppRollout\nmetadata:\n name: k8s-relay\n labels:\n app: k8s-relay\nsp"
},
{
"path": ".github/ci/deployments/robco-navtest/config.sh",
"chars": 493,
"preview": "#!/usr/bin/env bash\n\n# Enable google cloud robotics layer 2\nAPP_MANAGEMENT=true\n\nGCP_PROJECT_ID=robco-navtest\nGCP_REGION"
},
{
"path": ".github/ci/integration_test.sh",
"chars": 1989,
"preview": "#!/bin/bash\n\nDIR=\"$( cd \"$( dirname \"${BASH_SOURCE[0]}\" )\" && pwd )\"\nsource \"${DIR}/common.sh\"\nsource \"./scripts/common."
},
{
"path": ".github/ci/integration_test_cloudbuild.yaml",
"chars": 1038,
"preview": "# A Cloud Build job for running integration_test.sh.\n# TODO(b/323509860): Run directly on the Action runner when it supp"
},
{
"path": ".github/ci/integration_test_image_builder.sh",
"chars": 473,
"preview": "#!/bin/bash\n# Builds and pushes a docker image that can be used in Cloud Build to run\n# the integration test (see integr"
},
{
"path": ".github/ci/presubmit.sh",
"chars": 1534,
"preview": "#!/bin/bash\n#\n# Presubmit script for testing cloud robotics.\n# Expected to run remotely on a GitHub Actions runner, not "
},
{
"path": ".github/ci/release_binary.sh",
"chars": 2330,
"preview": "#!/bin/bash\n\nset -euo pipefail\n\nDIR=\"$( cd \"$( dirname \"${BASH_SOURCE[0]}\" )\" && pwd )\"\n# shellcheck source=ci/common.sh"
},
{
"path": ".github/dependabot.yml",
"chars": 302,
"preview": "version: 2\nupdates:\n - package-ecosystem: \"gomod\"\n directory: \"/src/\"\n schedule:\n interval: \"weekly\"\n\n - pa"
},
{
"path": ".github/workflows/check-bazel.yml",
"chars": 1332,
"preview": "name: Check Bazel\n\non:\n workflow_call:\n\npermissions:\n contents: read\n id-token: write\n\njobs:\n check-bazel:\n runs-"
},
{
"path": ".github/workflows/postsubmit.yml",
"chars": 1606,
"preview": "name: Postsubmit\n\non:\n schedule:\n - cron: \"0 4 * * *\" # Once a day at 4am.\n # Manual runs through Actions tab in th"
},
{
"path": ".github/workflows/presubmit.yml",
"chars": 2852,
"preview": "name: Presubmit\n\non:\n pull_request:\n branches: [\"main\"]\n workflow_dispatch:\n\npermissions:\n contents: read\n id-tok"
},
{
"path": ".github/workflows/release.yml",
"chars": 2605,
"preview": "name: Create release\n\non:\n schedule:\n - cron: \"0 5 * * *\" # Once a day at 5am.\n # Manual runs through Actions tab i"
},
{
"path": ".gitignore",
"chars": 160,
"preview": ".cache/\n*.pyc\nenv/\n\n# auto-generated by scripts/backport_kubeadm.sh\nnsenter\n\n# IntelliJ files\n.project/\n\n# Bazel\nbazel-*"
},
{
"path": ".pep8",
"chars": 53,
"preview": "[pep8]\naggressive=2\nindent-size=2\nmax-line-length=80\n"
},
{
"path": "BUILD.bazel",
"chars": 977,
"preview": "# Description:\n# Root BUILD file for cloud-robotics\n\nload(\"@bazel_gazelle//:def.bzl\", \"gazelle\")\n\npackage(default_visi"
},
{
"path": "CONTRIBUTING.md",
"chars": 1358,
"preview": "# How to Contribute\n\nWe'd love to accept your patches and contributions to this project. There are\njust a few small guid"
},
{
"path": "LICENSE",
"chars": 11358,
"preview": "\n Apache License\n Version 2.0, January 2004\n "
},
{
"path": "METADATA",
"chars": 341,
"preview": "name: \"Cloud Robotics\"\n\nthird_party {\n # https://nvd.nist.gov/products/cpe/search\n security {\n tag: \"NVD-CPE2.3:cpe"
},
{
"path": "MODULE.bazel",
"chars": 4291,
"preview": "bazel_dep(name = \"aspect_bazel_lib\", version = \"2.21.2\")\nbazel_dep(name = \"bazel_skylib\", version = \"1.8.1\")\nbazel_dep(n"
},
{
"path": "README.md",
"chars": 1731,
"preview": "# Cloud Robotics Core\n\nGoogle's Cloud Robotics Core is an open source platform that provides\ninfrastructure essential to"
},
{
"path": "bazel/BUILD.bazel",
"chars": 555,
"preview": "exports_files([\n \"app.bzl\",\n \"app_chart.bzl\",\n \"container_push.bzl\",\n \"repositories.bzl\",\n])\n\nplatform(\n "
},
{
"path": "bazel/BUILD.sysroot",
"chars": 1384,
"preview": "filegroup(\n name = \"all_files\",\n srcs = glob(\n [\n \"lib/x86_64-linux-gnu/ld*\",\n \"lib/x"
},
{
"path": "bazel/app.bzl",
"chars": 1471,
"preview": "load(\"//bazel/build_rules/app_chart:run_parallel.bzl\", \"run_parallel\")\n\ndef app(name, charts, visibility = None):\n \"\""
},
{
"path": "bazel/app_chart.bzl",
"chars": 5726,
"preview": "load(\"//bazel:build_rules/helm_chart.bzl\", \"helm_chart\")\nload(\"//bazel/build_rules/app_chart:cache_gcr_credentials.bzl\","
},
{
"path": "bazel/build_rules/app_chart/BUILD.bazel",
"chars": 182,
"preview": "exports_files([\n \"cache_gcr_credentials.sh.tpl\",\n \"Chart.yaml.template\",\n \"push_all.sh.tpl\",\n \"run_parallel."
},
{
"path": "bazel/build_rules/app_chart/Chart.yaml.template",
"chars": 109,
"preview": "apiVersion: v1\nname: ${name}\nversion: ${version}\n# Linter expects an icon.\nicon: https://google.com/icon.png\n"
},
{
"path": "bazel/build_rules/app_chart/cache_gcr_credentials.bzl",
"chars": 1833,
"preview": "def _get_runfile_path(ctx, f):\n \"\"\"Return the runfiles relative path of f.\"\"\"\n if ctx.workspace_name:\n retu"
},
{
"path": "bazel/build_rules/app_chart/cache_gcr_credentials.sh.tpl",
"chars": 1170,
"preview": "#!/usr/bin/env bash\n\nset -eu\n\nfunction guess_runfiles() {\n pushd ${BASH_SOURCE[0]}.runfiles > /dev/null 2>&1\n pwd\n"
},
{
"path": "bazel/build_rules/app_chart/push_all.bzl",
"chars": 2640,
"preview": "load(\"//bazel:container_push.bzl\", \"container_push\")\n\ndef _get_runfile_path(ctx, f):\n \"\"\"Return the runfiles relative"
},
{
"path": "bazel/build_rules/app_chart/push_all.sh.tpl",
"chars": 864,
"preview": "#!/usr/bin/env bash\n\nset -eu\n\nif [[ \"$#\" -lt 1 ]]; then\n echo \"Usage: $0 <container-registry>\"\n exit 1\nfi\nCONTAINER_RE"
},
{
"path": "bazel/build_rules/app_chart/run_parallel.bzl",
"chars": 1584,
"preview": "def _get_runfile_path(ctx, f):\n \"\"\"Return the runfiles relative path of f.\"\"\"\n if ctx.workspace_name:\n retu"
},
{
"path": "bazel/build_rules/app_chart/run_parallel.sh.tpl",
"chars": 530,
"preview": "#!/usr/bin/env bash\n\nset -eu\n\nfunction guess_runfiles() {\n pushd ${BASH_SOURCE[0]}.runfiles > /dev/null 2>&1\n pwd\n"
},
{
"path": "bazel/build_rules/app_chart/values-cloud.yaml",
"chars": 200,
"preview": "domain: \"example.com\"\nproject: \"my-gcp-project\"\ndeploy_environment: \"GCP\"\nregistry: \"gcr.io/my-gcp-project\"\nrobots: []\nr"
},
{
"path": "bazel/build_rules/app_chart/values-robot.yaml",
"chars": 127,
"preview": "domain: \"example.com\"\nproject: \"my-gcp-project\"\ndeploy_environment: \"GCP\"\nregistry: \"gcr.io/my-gcp-project\"\n\nrobot:\n na"
},
{
"path": "bazel/build_rules/copy.bzl",
"chars": 864,
"preview": "\"\"\"Macros to help rearrange files.\"\"\"\n\ndef copy_files(name, srcs, outdir, visibility = None):\n \"\"\"Creates copies of f"
},
{
"path": "bazel/build_rules/helm_chart.bzl",
"chars": 1893,
"preview": "def helm_chart(ctx, name, chart, files, templates, values, version, helm, out):\n \"\"\"Starlark function that builds a h"
},
{
"path": "bazel/build_rules/helm_template.bzl",
"chars": 1148,
"preview": "def helm_template(name, release_name, chart, values, namespace = None, helm_version = 2):\n \"\"\"Locally expand a helm c"
},
{
"path": "bazel/container_push.bzl",
"chars": 448,
"preview": "load(\"@rules_oci//oci:defs.bzl\", \"oci_push\")\n\ndef container_push(*args, **kwargs):\n \"\"\"Creates a script to push a con"
},
{
"path": "bazel/debug_repository.bzl",
"chars": 830,
"preview": "\"\"\"Debug util for repository definitions.\"\"\"\n\ndef debug_repository(repo, *fields):\n \"\"\"debug_repository(repo) identif"
},
{
"path": "config.sh.tmpl",
"chars": 2228,
"preview": "#!/usr/bin/env bash\n\n### Required settings ###\n\n# Project ID of your Cloud Robotics GCP project. This project can be cre"
},
{
"path": "current_versions.txt",
"chars": 126,
"preview": "{\n \"cert-manager\": \"1.16.3\",\n \"ingress-nginx\": \"1.8.4\",\n \"oauth2-proxy\": \"7.5.1\",\n \"stackdriver-logging-agent\": \"1.9"
},
{
"path": "deploy.sh",
"chars": 14547,
"preview": "#!/bin/bash\n#\n# Copyright 2019 The Cloud Robotics Authors\n#\n# Licensed under the Apache License, Version 2.0 (the \"Licen"
},
{
"path": "docs/.gitignore",
"chars": 188,
"preview": "# Files created when running Jekyll locally, following\n# https://help.github.com/en/articles/setting-up-your-github-page"
},
{
"path": "docs/_config.yml",
"chars": 26,
"preview": "theme: jekyll-theme-slate\n"
},
{
"path": "docs/concepts/app-management.md",
"chars": 3741,
"preview": "# App Management\n\nThe Cloud Robotics Core application management (Layer 2) makes it easy to define and deploy\narbitrary "
},
{
"path": "docs/concepts/config.md",
"chars": 917,
"preview": "# Project configuration\n\nThe project configuration that one has entered during the initial setup is\nstored with the proj"
},
{
"path": "docs/concepts/device_identity.md",
"chars": 2095,
"preview": "# Device Identity\n\nDevice Identity, part of Layer 1, provides an identity for robot clusters and\nservices to integrate t"
},
{
"path": "docs/concepts/federation.md",
"chars": 7315,
"preview": "# Federation\n\nFederation, part of Layer 1, is responsible for synchronizing the state between robot and cloud\nclusters. "
},
{
"path": "docs/developers/debug-auth.md",
"chars": 4585,
"preview": "# Debugging authentication problems\n\nUseful tips for working with Authentication and Authorization systems.\n\n## Run a sa"
},
{
"path": "docs/how-to/connecting-robot.md",
"chars": 2766,
"preview": "# Connecting a robot to the cloud\n\nEstimated time: 10 min\n\nThis page describes how to connect a Kubernetes cluster on a "
},
{
"path": "docs/how-to/creating-declarative-api.md",
"chars": 20006,
"preview": "# Creating a declarative API\n\n<!-- Estimated time: TODO -->\n\nIn this guide we will use a Kubernetes-style declarative AP"
},
{
"path": "docs/how-to/deploy-from-sources.md",
"chars": 4980,
"preview": "# Deploy Cloud Robotics Core from sources\n\nEstimated time: 30 min\n\nThis page describes how to set up a Google Cloud Plat"
},
{
"path": "docs/how-to/deploying-grpc-service.md",
"chars": 10988,
"preview": "# Deploying a gRPC service written in C++\n\nEstimated time: 60 min\n\nIn this guide we will deploy a gRPC service written i"
},
{
"path": "docs/how-to/deploying-service.md",
"chars": 14325,
"preview": "# Deploying a service to the cloud cluster\n\nEstimated time: 60 min\n\nIn this guide we will write a HTTP service in Python"
},
{
"path": "docs/how-to/examples/charge-service/Dockerfile",
"chars": 92,
"preview": "FROM python:alpine\n\nWORKDIR /data\n\nCOPY server.py ./\n\nCMD [ \"python\", \"-u\", \"./server.py\" ]\n"
},
{
"path": "docs/how-to/examples/charge-service/charge-action.yaml",
"chars": 81,
"preview": "apiVersion: example.com/v1\nkind: ChargeAction\nmetadata:\n name: my-charge-action\n"
},
{
"path": "docs/how-to/examples/charge-service/charge-controller.yaml",
"chars": 843,
"preview": "apiVersion: metacontroller.k8s.io/v1alpha1\nkind: CompositeController\nmetadata:\n name: charge-controller\nspec:\n generat"
},
{
"path": "docs/how-to/examples/charge-service/charge-crd.yaml",
"chars": 838,
"preview": "apiVersion: apiextensions.k8s.io/v1\nkind: CustomResourceDefinition\nmetadata:\n name: chargeactions.example.com\n annotat"
},
{
"path": "docs/how-to/examples/charge-service/server.py",
"chars": 3714,
"preview": "from http.server import BaseHTTPRequestHandler, HTTPServer\nimport json\nimport signal\nimport sys\nimport time\nimport uuid\n"
},
{
"path": "docs/how-to/examples/greeter-service/Makefile",
"chars": 1727,
"preview": "#\n# Copyright 2019 The Cloud Robotics Authors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you "
},
{
"path": "docs/how-to/examples/greeter-service/client/Dockerfile",
"chars": 188,
"preview": "FROM grpc/cxx:1.12.0\n\nWORKDIR /data\n\nCOPY client/client.cc ./client/\nCOPY proto/helloworld.proto ./proto/\nCOPY Makefile "
},
{
"path": "docs/how-to/examples/greeter-service/client/client.cc",
"chars": 3500,
"preview": "/*\n *\n * Copyright 2019 The Cloud Robotics Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");"
},
{
"path": "docs/how-to/examples/greeter-service/deploy.sh",
"chars": 1783,
"preview": "#!/bin/bash\n#\n# Copyright 2019 The Cloud Robotics Authors\n#\n# Licensed under the Apache License, Version 2.0 (the \"Licen"
},
{
"path": "docs/how-to/examples/greeter-service/greeter-server.yaml.tmpl",
"chars": 1598,
"preview": "apiVersion: networking.k8s.io/v1\nkind: Ingress\nmetadata:\n name: greeter-server-ingress\n annotations:\n nginx.ingress"
},
{
"path": "docs/how-to/examples/greeter-service/proto/helloworld.proto",
"chars": 971,
"preview": "// Copyright 2019 The Cloud Robotics Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// yo"
},
{
"path": "docs/how-to/examples/greeter-service/server/Dockerfile",
"chars": 188,
"preview": "FROM grpc/cxx:1.12.0\n\nWORKDIR /data\n\nCOPY server/server.cc ./server/\nCOPY proto/helloworld.proto ./proto/\nCOPY Makefile "
},
{
"path": "docs/how-to/examples/greeter-service/server/server.cc",
"chars": 2472,
"preview": "/*\n *\n * Copyright 2019 The Cloud Robotics Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");"
},
{
"path": "docs/how-to/examples/hello-service/client/Dockerfile",
"chars": 145,
"preview": "FROM python:alpine\n\nRUN pip install --no-cache-dir google-auth requests\n\nWORKDIR /data\n\nCOPY client.py ./\n\nCMD [ \"python"
},
{
"path": "docs/how-to/examples/hello-service/client/client.py",
"chars": 349,
"preview": "import google.auth\nimport google.auth.transport.requests as requests\n\ncredentials, project_id = google.auth.default()\n\na"
},
{
"path": "docs/how-to/examples/hello-service/server/Dockerfile",
"chars": 92,
"preview": "FROM python:alpine\n\nWORKDIR /data\n\nCOPY server.py ./\n\nCMD [ \"python\", \"-u\", \"./server.py\" ]\n"
},
{
"path": "docs/how-to/examples/hello-service/server/hello-server.yaml",
"chars": 1278,
"preview": "apiVersion: networking.k8s.io/v1\nkind: Ingress\nmetadata:\n name: hello-server-ingress\n annotations:\n nginx.ingress.k"
},
{
"path": "docs/how-to/examples/hello-service/server/server.py",
"chars": 584,
"preview": "from http import server\nimport signal\nimport sys\n\n\nclass MyRequestHandler(server.BaseHTTPRequestHandler):\n def do_GET(s"
},
{
"path": "docs/how-to/running-ros-node.md",
"chars": 3598,
"preview": "# Running a ROS node as a Kubernetes deployment\n\nEstimated time: 10 min\n\nThe following instructions describe how to setu"
},
{
"path": "docs/how-to/setting-up-oauth.md",
"chars": 2781,
"preview": "# Setting up OAuth for web UIs\n\nEstimated time: 5 min\n\nWhen a user loads a web UI hosted in the cloud Kubernetes cluster"
},
{
"path": "docs/how-to/using-cloud-storage.md",
"chars": 4564,
"preview": "# Using Cloud Storage from a robot\n\nEstimated time: 20 minutes\n\nThis page describes a simple Cloud Storage transaction t"
},
{
"path": "docs/index.md",
"chars": 2324,
"preview": "Google's Cloud Robotics Core is an open source platform that provides\ninfrastructure essential to building and running r"
},
{
"path": "docs/overview.md",
"chars": 4854,
"preview": "# Overview of Cloud Robotics Core\n\nTo understand Cloud Robotics Core, you should be familiar with the following concepts"
},
{
"path": "docs/quickstart.md",
"chars": 5452,
"preview": "# Quickstart\n\nEstimated time: 10 min\n\nThis page describes how to set up a Google Cloud Platform (GCP) project\ncontaining"
},
{
"path": "new_versions.txt",
"chars": 127,
"preview": "{\n \"cert-manager\": \"1.13.2\",\n \"ingress-nginx\": \"1.9.4\",\n \"oauth2-proxy\": \"7.5.1\",\n \"stackdriver-logging-agent\": \"1.1"
},
{
"path": "non_module_deps.bzl",
"chars": 3291,
"preview": "load(\"@bazel_tools//tools/build_defs/repo:http.bzl\", \"http_archive\")\n# -- load statements -- #\n\ndef _non_module_deps_imp"
},
{
"path": "nvchecker.toml",
"chars": 1211,
"preview": "[__config__]\noldver = \"current_versions.txt\"\nnewver = \"new_versions.txt\"\n\n# containers\n# git grep -E \"^\\s+image: \" *.yam"
},
{
"path": "scripts/BUILD.bazel",
"chars": 99,
"preview": "exports_files([\n \"common.sh\",\n \"config.sh\",\n \"include-config.sh\",\n \"set-config.sh\",\n])\n"
},
{
"path": "scripts/backup_robots.sh",
"chars": 1658,
"preview": "#!/bin/bash\n#\n# Copyright 2021 The Cloud Robotics Authors\n#\n# Licensed under the Apache License, Version 2.0 (the \"Licen"
},
{
"path": "scripts/check-images.sh",
"chars": 2478,
"preview": "#!/bin/bash\n#\n# Copyright 2019 The Cloud Robotics Authors\n#\n# Licensed under the Apache License, Version 2.0 (the \"Licen"
},
{
"path": "scripts/common.sh",
"chars": 2682,
"preview": "#!/bin/bash\n#\n# Copyright 2019 The Cloud Robotics Authors\n#\n# Licensed under the Apache License, Version 2.0 (the \"Licen"
},
{
"path": "scripts/config.sh",
"chars": 1721,
"preview": "#!/bin/bash\n#\n# Copyright 2024 The Cloud Robotics Authors\n#\n# Licensed under the Apache License, Version 2.0 (the \"Licen"
},
{
"path": "scripts/include-config.sh",
"chars": 2245,
"preview": "#!/usr/bin/env bash\n#\n# Copyright 2019 The Cloud Robotics Authors\n#\n# Licensed under the Apache License, Version 2.0 (th"
},
{
"path": "scripts/migrate.sh",
"chars": 5431,
"preview": "#!/bin/bash\n#\n# Copyright 2025 The Cloud Robotics Authors\n#\n# Licensed under the Apache License, Version 2.0 (the \"Licen"
},
{
"path": "scripts/pre-commit",
"chars": 5996,
"preview": "#!/bin/bash\n# git hook to ensure code style\n# ln -sf ../../scripts/pre-commit .git/hooks/\n\n# shellcheck disable=2044,204"
},
{
"path": "scripts/robot-sim.sh",
"chars": 3494,
"preview": "#!/bin/bash\n#\n# Copyright 2019 The Cloud Robotics Authors\n#\n# Licensed under the Apache License, Version 2.0 (the \"Licen"
},
{
"path": "scripts/set-config.sh",
"chars": 12130,
"preview": "#!/bin/bash\n#\n# Copyright 2019 The Cloud Robotics Authors\n#\n# Licensed under the Apache License, Version 2.0 (the \"Licen"
},
{
"path": "src/.gitignore",
"chars": 8,
"preview": ".gopath\n"
},
{
"path": "src/BUILD.bazel",
"chars": 40,
"preview": "exports_files(\n srcs = [\"go.mod\"],\n)\n"
},
{
"path": "src/README.md",
"chars": 2305,
"preview": "# Source Code\n\nThis directory contains the source code for Cloud Robotics Core components. Most\ncomponents are written i"
},
{
"path": "src/app_charts/BUILD.bazel",
"chars": 920,
"preview": "load(\"//bazel/build_rules/app_chart:cache_gcr_credentials.bzl\", \"cache_gcr_credentials\")\nload(\"//bazel/build_rules/app_c"
},
{
"path": "src/app_charts/README.md",
"chars": 478,
"preview": "# Testing instructions\n\nUse `bazel run :push` on the app directory to build & upload Docker images,\nupload Helm charts a"
},
{
"path": "src/app_charts/akri/BUILD.bazel",
"chars": 801,
"preview": "load(\"//bazel:app.bzl\", \"app\")\nload(\"//bazel:app_chart.bzl\", \"app_chart\")\nload(\"//bazel:build_rules/helm_template.bzl\", "
},
{
"path": "src/app_charts/akri/akri-robot.values.yaml",
"chars": 404,
"preview": "# kubernetesDistro describes the Kubernetes distro Akri is running on. It is used to conditionally set\n# distribution sp"
},
{
"path": "src/app_charts/akri/robot/akri.yaml",
"chars": 334,
"preview": "# This includes all resources expanded from the akri chart using\n# the values in ../values.yaml.\n# Some pseudo-variables"
},
{
"path": "src/app_charts/akri/values-robot.yaml",
"chars": 112,
"preview": "domain: \"example.com\"\nproject: \"my-gcp-project\"\nregistry: \"gcr.io/my-gcp-project\"\nrobots: []\n\nudev:\n rules: []\n"
},
{
"path": "src/app_charts/base/BUILD.bazel",
"chars": 3597,
"preview": "load(\"@rules_shell//shell:sh_test.bzl\", \"sh_test\")\nload(\"//bazel:app_chart.bzl\", \"app_chart\")\nload(\"//bazel:build_rules/"
},
{
"path": "src/app_charts/base/README.md",
"chars": 318,
"preview": "# Making changes to the `fluent-bit` configmap\n\nIf you want to change the `fluent-bit` spec, do not edit the autogenerat"
},
{
"path": "src/app_charts/base/app_management_test.sh",
"chars": 2867,
"preview": "#!/usr/bin/env bash\n#\n# Copyright 2019 The Cloud Robotics Authors\n#\n# Licensed under the Apache License, Version 2.0 (th"
},
{
"path": "src/app_charts/base/cert-manager-cloud.values.yaml",
"chars": 446,
"preview": "# Configuration for the cert-manager chart.\n# Reference: https://github.com/jetstack/cert-manager/blob/master/deploy/cha"
},
{
"path": "src/app_charts/base/cert-manager-google-cas-issuer-cloud.values.yaml",
"chars": 899,
"preview": "# Configuration for the cert-manager chart.\n# Reference: https://github.com/jetstack/google-cas-issuer/blob/main/deploy/"
},
{
"path": "src/app_charts/base/cert-manager-robot.values.yaml",
"chars": 867,
"preview": "# Configuration for the cert-manager chart.\n# Reference: https://github.com/jetstack/cert-manager/blob/master/deploy/cha"
},
{
"path": "src/app_charts/base/cloud/app-management-policy.yaml",
"chars": 4249,
"preview": "# This policy lets app-rollout and chart-assigment controllers operate on the\n# apps & registry CRDs. It also grants cha"
},
{
"path": "src/app_charts/base/cloud/app-management.yaml",
"chars": 6211,
"preview": "{{ if eq .Values.app_management \"true\" }}\n# app-rollout-controller\napiVersion: apps/v1\nkind: Deployment\nmetadata:\n name"
},
{
"path": "src/app_charts/base/cloud/apps-crd.yaml",
"chars": 6965,
"preview": "apiVersion: apiextensions.k8s.io/v1\nkind: CustomResourceDefinition\nmetadata:\n name: apps.apps.cloudrobotics.com\n annot"
},
{
"path": "src/app_charts/base/cloud/cert-ingress.yaml",
"chars": 331,
"preview": "# Owns the 'tls' block to ensure a cert is generated and avoids repetition of \n# the 'tls' block in other ingresses. Cer"
},
{
"path": "src/app_charts/base/cloud/cert-manager-certificates.yaml",
"chars": 899,
"preview": "apiVersion: cert-manager.io/v1\nkind: Certificate\nmetadata:\n name: selfsigned-ca\nspec:\n isCA: true\n duration: 8760h #"
},
{
"path": "src/app_charts/base/cloud/cert-manager-google-cas-issuer.yaml",
"chars": 438,
"preview": "{{- if eq .Values.certificate_provider \"google-cas\" }}\n# This includes all resources expanded from the cert-manager char"
},
{
"path": "src/app_charts/base/cloud/cert-manager-issuers.yaml",
"chars": 1438,
"preview": "# A self-signing issuer for cluster-internal services.\napiVersion: cert-manager.io/v1\nkind: ClusterIssuer\nmetadata:\n na"
},
{
"path": "src/app_charts/base/cloud/cert-manager.yaml",
"chars": 354,
"preview": "# This includes all resources expanded from the cert-manager chart using\n# the values in ../cert-manager-cloud.values.ya"
},
{
"path": "src/app_charts/base/cloud/cr-syncer-auth-webhook.yaml",
"chars": 1709,
"preview": "{{ if eq .Values.onprem_federation \"true\" }}\n# The cr-syncer-auth-webhook verifies that requests from the cr-syncer are\n"
},
{
"path": "src/app_charts/base/cloud/cr-syncer-policy.yaml",
"chars": 2147,
"preview": "{{ if eq .Values.onprem_federation \"true\" }}\n# This policy lets the cr-syncer operate on the apps & registry CRDs.\napiVe"
},
{
"path": "src/app_charts/base/cloud/domain-redirect.yaml",
"chars": 628,
"preview": "{{ $endpointsURL := print \"www.endpoints.\" .Values.project \".cloud.goog\" }}\n{{ if ne $endpointsURL .Values.domain }}\napi"
},
{
"path": "src/app_charts/base/cloud/fluentd-metrics.yaml",
"chars": 764,
"preview": "# Adds a Prometheus ServiceMonitor for scraping the fluentd metrics.\n# By default, google-fluentd exports some Prometheu"
},
{
"path": "src/app_charts/base/cloud/kubernetes-api.yaml",
"chars": 835,
"preview": "{{ if eq .Values.onprem_federation \"true\" }}\napiVersion: networking.k8s.io/v1\nkind: Ingress\nmetadata:\n name: kubernetes"
},
{
"path": "src/app_charts/base/cloud/namespace.yaml",
"chars": 134,
"preview": "apiVersion: v1\nkind: Namespace\nmetadata:\n name: {{ .Release.Namespace }}\n labels:\n certmanager.k8s.io/disable-valid"
},
{
"path": "src/app_charts/base/cloud/nginx-ingress-controller-policy.yaml",
"chars": 3629,
"preview": "# Source: ingress-nginx/templates/controller-serviceaccount.yaml\napiVersion: v1\nkind: ServiceAccount\nmetadata:\n name: i"
},
{
"path": "src/app_charts/base/cloud/nginx-ingress-controller.yaml",
"chars": 5456,
"preview": "apiVersion: v1\nkind: ConfigMap\nmetadata:\n name: nginx-ingress-controller\ndata:\n # This is the same as the default but "
},
{
"path": "src/app_charts/base/cloud/oauth2-proxy.yaml",
"chars": 2553,
"preview": "{{ if and (ne .Values.oauth2_proxy.client_id \"\") (ne .Values.oauth2_proxy.client_secret \"\") }}\napiVersion: apps/v1\nkind:"
},
{
"path": "src/app_charts/base/cloud/registry-crd.yaml",
"chars": 2793,
"preview": "apiVersion: apiextensions.k8s.io/v1\nkind: CustomResourceDefinition\nmetadata:\n name: robottypes.registry.cloudrobotics.c"
},
{
"path": "src/app_charts/base/cloud/registry-policy.yaml",
"chars": 694,
"preview": "# This policy lets the human-acl GCP SA register robots. For context, see the\n# IAM policy in service-account.tf.\napiVer"
},
{
"path": "src/app_charts/base/cloud/relay-dashboards.yaml",
"chars": 188,
"preview": "apiVersion: v1\nkind: ConfigMap\nmetadata:\n name: relay-dashboards-json\n labels:\n grafana: \"1\"\ndata:\n relay-dashboar"
},
{
"path": "src/app_charts/base/cloud/token-vendor-app-fwd.yaml",
"chars": 449,
"preview": "# The Token Vendor was moved to the app namespace.\n# We create this service here in default namespace \n# as some codepat"
},
{
"path": "src/app_charts/base/cloud/token-vendor-rollout.yaml",
"chars": 170,
"preview": "apiVersion: apps.cloudrobotics.com/v1alpha1\nkind: AppRollout\nmetadata:\n name: token-vendor\n labels:\n app: token-ven"
},
{
"path": "src/app_charts/base/fluent-bit-helm.sh",
"chars": 1558,
"preview": "#!/bin/bash\n# needs at least helm v3.5.0\nOUTPUT=./robot/fluent-bit.yaml\nTEMPLATE_VERSION=0.48.9\nhelm repo add fluent htt"
},
{
"path": "src/app_charts/base/fluent-bit-values.yaml",
"chars": 7563,
"preview": "image:\n pullPolicy: IfNotPresent\n\nenv:\n - name: MY_NODE_NAME\n valueFrom:\n fieldRef:\n fieldPath: spec.no"
},
{
"path": "src/app_charts/base/relay-dashboard.json",
"chars": 32382,
"preview": "{\n \"annotations\": {\n \"list\": [\n {\n \"builtIn\": 1,\n \"datasource\": {\n \"type\": \"datasource\","
},
{
"path": "src/app_charts/base/robot/app-management.yaml",
"chars": 3019,
"preview": "{{ if eq .Values.app_management \"true\" }}\napiVersion: apps/v1\nkind: Deployment\nmetadata:\n name: chart-assignment-contro"
},
{
"path": "src/app_charts/base/robot/cert-manager-certificates.yaml",
"chars": 364,
"preview": "apiVersion: cert-manager.io/v1\nkind: Certificate\nmetadata:\n name: selfsigned-ca\nspec:\n isCA: true\n duration: 8760h # "
},
{
"path": "src/app_charts/base/robot/cert-manager-issuers.yaml",
"chars": 302,
"preview": "# A self-signing issuer for cluster-internal services.\napiVersion: cert-manager.io/v1\nkind: ClusterIssuer\nmetadata:\n na"
},
{
"path": "src/app_charts/base/robot/cert-manager.yaml",
"chars": 367,
"preview": "{{ if eq .Values.app_management \"true\" }}\n# This includes all resources expanded from the cert-manager chart using\n# the"
},
{
"path": "src/app_charts/base/robot/cr-syncer.yaml",
"chars": 1235,
"preview": "{{ if eq .Values.cr_syncer \"true\" }}\napiVersion: apps/v1\nkind: Deployment\nmetadata:\n name: cr-syncer\nspec:\n replicas: "
},
{
"path": "src/app_charts/base/robot/fluent-bit.yaml",
"chars": 13253,
"preview": "# !!! DO NOT EDIT THIS FILE !!!\n# This file is autogenerated using src/app_charts/base/fluent-bit-helm.sh.\n# See src/app"
},
{
"path": "src/app_charts/base/robot/fluentd-gcp-addon.yaml",
"chars": 246,
"preview": "{{ if and (eq .Values.robot_authentication \"true\") (eq .Values.fluentd \"true\") }}\n{{ .Files.Get \"files/fluentd-gcp-confi"
},
{
"path": "src/app_charts/base/robot/fluentd-metrics.yaml",
"chars": 806,
"preview": "# Adds a Prometheus ServiceMonitor for scraping the fluentd metrics.\n# By default, google-fluentd exports some Prometheu"
},
{
"path": "src/app_charts/base/robot/gcr-credential-refresher.yaml",
"chars": 1249,
"preview": "{{ if and (eq .Values.robot_authentication \"true\") (ne .Values.project \"\") (eq .Values.running_on_gke \"false\") }}\napiVer"
},
{
"path": "src/app_charts/base/robot/metadata-server.yaml",
"chars": 1564,
"preview": "{{ if and (eq .Values.robot_authentication \"true\") (ne .Values.project \"\") }}\napiVersion: apps/v1\nkind: DaemonSet\nmetada"
},
{
"path": "src/app_charts/base/values-cloud.yaml",
"chars": 758,
"preview": "domain: \"example.com\"\ningress_ip: \"\"\nproject: \"my-gcp-project\"\nregion: \"us-north1-a\"\ndeploy_environment: \"GCP\"\nregistry:"
},
{
"path": "src/app_charts/base/values-robot.yaml",
"chars": 1306,
"preview": "domain: \"example.com\"\nproject: \"my-gcp-project\"\ndeploy_environment: \"GCP\"\nregistry: \"gcr.io/my-gcp-project\"\n\n# Setting a"
},
{
"path": "src/app_charts/k8s-relay/BUILD.bazel",
"chars": 569,
"preview": "load(\"//bazel:app.bzl\", \"app\")\nload(\"//bazel:app_chart.bzl\", \"app_chart\")\n\napp_chart(\n name = \"k8s-relay-cloud\",\n "
},
{
"path": "src/app_charts/k8s-relay/cloud/ingress.yaml",
"chars": 1686,
"preview": "apiVersion: networking.k8s.io/v1\nkind: Ingress\nmetadata:\n name: kubernetes-relay-client\n annotations:\n nginx.ingres"
},
{
"path": "src/app_charts/k8s-relay/cloud/kubernetes-relay-server.yaml",
"chars": 1101,
"preview": "apiVersion: apps/v1\nkind: Deployment\nmetadata:\n name: kubernetes-relay-server\nspec:\n replicas: 1\n selector:\n match"
},
{
"path": "src/app_charts/k8s-relay/cloud/service-monitor.yaml",
"chars": 529,
"preview": "apiVersion: monitoring.coreos.com/v1\nkind: ServiceMonitor\nmetadata:\n name: kubernetes-relay-server\n labels:\n promet"
},
{
"path": "src/app_charts/k8s-relay/cloud/service.yaml",
"chars": 301,
"preview": "apiVersion: v1\nkind: Service\nmetadata:\n name: kubernetes-relay-server\n labels:\n # This is used by the ServiceMonito"
},
{
"path": "src/app_charts/k8s-relay/robot/kubernetes-relay-client.yaml",
"chars": 1181,
"preview": "apiVersion: apps/v1\nkind: Deployment\nmetadata:\n name: kubernetes-relay-client\nspec:\n replicas: 1\n selector:\n match"
},
{
"path": "src/app_charts/k8s-relay/values-cloud.yaml",
"chars": 324,
"preview": "domain: \"example.com\"\nproject: \"my-gcp-project\"\nregistry: \"gcr.io/my-gcp-project\"\nrobots: []\n\n# MetricRelabelConfigs to "
},
{
"path": "src/app_charts/k8s-relay/values-robot.yaml",
"chars": 90,
"preview": "kubernetes_relay_client:\n resources:\n requests:\n memory: \"10Mi\"\n cpu: \"50m\"\n"
},
{
"path": "src/app_charts/mission-crd/BUILD.bazel",
"chars": 468,
"preview": "load(\"//bazel:app.bzl\", \"app\")\nload(\"//bazel:app_chart.bzl\", \"app_chart\")\n\napp_chart(\n name = \"mission-crd-robot\",\n "
},
{
"path": "src/app_charts/mission-crd/mission_crd.yaml",
"chars": 4895,
"preview": "apiVersion: apiextensions.k8s.io/v1\nkind: CustomResourceDefinition\nmetadata:\n annotations:\n cr-syncer.cloudrobotics."
},
{
"path": "src/app_charts/mission-crd/values.yaml",
"chars": 105,
"preview": "project: \"my-gcp-project\"\nregistry: \"gcr.io/my-gcp-project\"\n\nrobot:\n name: \"\"\n\ncrd_spec_source: \"cloud\"\n"
},
{
"path": "src/app_charts/platform-apps/BUILD.bazel",
"chars": 234,
"preview": "load(\"//bazel:app_chart.bzl\", \"app_chart\")\n\napp_chart(\n name = \"platform-apps-cloud\",\n extra_templates = [\n "
},
{
"path": "src/app_charts/platform-apps/values.yaml",
"chars": 108,
"preview": "domain: \"example.com\"\nproject: \"my-gcp-project\"\ndeploy_environment: \"GCP\"\nregistry: \"gcr.io/my-gcp-project\"\n"
},
{
"path": "src/app_charts/prometheus/BUILD.bazel",
"chars": 1869,
"preview": "load(\"//bazel:app.bzl\", \"app\")\nload(\"//bazel:app_chart.bzl\", \"app_chart\")\nload(\"//bazel:build_rules/helm_template.bzl\", "
},
{
"path": "src/app_charts/prometheus/README.md",
"chars": 858,
"preview": "# Prometheus App\n\nThe Prometheus app uses the upstream Helm chart\n`prometheus-community/prometheus-operator` as a basis "
},
{
"path": "src/app_charts/prometheus/cloud/app.yaml",
"chars": 8205,
"preview": "apiVersion: app.k8s.io/v1beta1\nkind: Application\nmetadata:\n name: \"prometheus\"\n labels:\n app.kubernetes.io/name: {{"
},
{
"path": "src/app_charts/prometheus/cloud/base-alerts.yaml",
"chars": 839,
"preview": "apiVersion: monitoring.coreos.com/v1\nkind: PrometheusRule\nmetadata:\n labels:\n app.kubernetes.io/name: {{ .Chart.Name"
},
{
"path": "src/app_charts/prometheus/cloud/federation-service-monitor.yaml",
"chars": 1302,
"preview": "{{ range .Values.robots }}\napiVersion: monitoring.coreos.com/v1\nkind: ServiceMonitor\nmetadata:\n name: prometheus-federa"
},
{
"path": "src/app_charts/prometheus/cloud/grafana-ingress.yaml",
"chars": 928,
"preview": "apiVersion: networking.k8s.io/v1\nkind: Ingress\nmetadata:\n name: grafana\n labels:\n app.kubernetes.io/name: {{ .Chart"
},
{
"path": "src/app_charts/prometheus/cloud/prometheus-ingress.yaml",
"chars": 831,
"preview": "apiVersion: networking.k8s.io/v1\nkind: Ingress\nmetadata:\n name: prometheus\n labels:\n app.kubernetes.io/name: {{ .Ch"
},
{
"path": "src/app_charts/prometheus/cloud/prometheus-operator.yaml",
"chars": 3463,
"preview": "# This includes all resources expanded from the prometheus-operator chart using\n# the values in ../prometheus-cloud.valu"
},
{
"path": "src/app_charts/prometheus/cloud/prometheus-relay.yaml",
"chars": 2705,
"preview": "apiVersion: apps/v1\nkind: Deployment\nmetadata:\n name: prometheus-relay-server\n labels:\n app.kubernetes.io/name: {{ "
},
{
"path": "src/app_charts/prometheus/cloud/storage-class.yaml",
"chars": 216,
"preview": "apiVersion: storage.k8s.io/v1\nkind: StorageClass\nmetadata:\n name: ssd\n labels:\n app.kubernetes.io/name: {{ .Chart.N"
},
{
"path": "src/app_charts/prometheus/prometheus-cloud.values.yaml",
"chars": 6551,
"preview": "# Configuration for the prometheus-operator chart.\n# Reference: \n# https://github.com/prometheus-community/helm-charts/b"
},
{
"path": "src/app_charts/prometheus/prometheus-robot.values.yaml",
"chars": 7547,
"preview": "# Configuration for the prometheus-operator chart.\n# Reference:\n# https://github.com/prometheus-community/helm-charts/bl"
},
{
"path": "src/app_charts/prometheus/robot/hw-exporter.yaml",
"chars": 1714,
"preview": "apiVersion: apps/v1\nkind: DaemonSet\nmetadata:\n name: hw-exporter\nspec:\n selector:\n matchLabels:\n app: hw-expor"
},
{
"path": "src/app_charts/prometheus/robot/prometheus-adapter.yaml",
"chars": 9816,
"preview": "apiVersion: apiregistration.k8s.io/v1\nkind: APIService\nmetadata:\n labels:\n app.kubernetes.io/component: metrics-adap"
},
{
"path": "src/app_charts/prometheus/robot/prometheus-operator.yaml",
"chars": 425,
"preview": "# This includes all resources expanded from the prometheus-operator chart using\n# the values in ../prometheus-cloud.valu"
},
{
"path": "src/app_charts/prometheus/robot/prometheus-relay-client.yaml",
"chars": 994,
"preview": "apiVersion: apps/v1\nkind: Deployment\nmetadata:\n name: prometheus-relay-client\nspec:\n replicas: 1\n selector:\n match"
},
{
"path": "src/app_charts/prometheus/robot/smartctl-exporter.yaml",
"chars": 1149,
"preview": "apiVersion: apps/v1\nkind: DaemonSet\nmetadata:\n name: smartctl-exporter\nspec:\n selector:\n matchLabels:\n app: sm"
},
{
"path": "src/app_charts/prometheus/update_prometheus_adapter.sh",
"chars": 539,
"preview": "#!/bin/bash\n\nVERSION=\"0.12.0\"\nOUT=\"robot/prometheus-adapter.yaml\"\nwget https://github.com/kubernetes-sigs/prometheus-ada"
},
{
"path": "src/app_charts/prometheus/values-cloud.yaml",
"chars": 2896,
"preview": "domain: \"example.com\"\nproject: \"my-gcp-project\"\nregistry: \"gcr.io/my-gcp-project\"\nrobots: []\n\n# The default requests/lim"
},
{
"path": "src/app_charts/token-vendor/BUILD.bazel",
"chars": 395,
"preview": "load(\"//bazel:app.bzl\", \"app\")\nload(\"//bazel:app_chart.bzl\", \"app_chart\")\n\napp_chart(\n name = \"token-vendor-cloud\",\n "
},
{
"path": "src/app_charts/token-vendor/cloud/dashboard.yaml",
"chars": 178,
"preview": "apiVersion: v1\nkind: ConfigMap\nmetadata:\n name: tokenvendor-dashboards-json\n labels:\n grafana: \"1\"\ndata:\n relay.js"
},
{
"path": "src/app_charts/token-vendor/cloud/ingress.yaml",
"chars": 2082,
"preview": "apiVersion: networking.k8s.io/v1\nkind: Ingress\nmetadata:\n name: public-key-access\n annotations:\n nginx.ingress.kube"
},
{
"path": "src/app_charts/token-vendor/cloud/service-monitor.yaml",
"chars": 325,
"preview": "apiVersion: monitoring.coreos.com/v1\nkind: ServiceMonitor\nmetadata:\n name: token-vendor\n labels:\n prometheus: kube-"
},
{
"path": "src/app_charts/token-vendor/cloud/service.yaml",
"chars": 275,
"preview": "apiVersion: v1\nkind: Service\nmetadata:\n name: token-vendor\n labels:\n # This is used by the ServiceMonitor.\n app:"
},
{
"path": "src/app_charts/token-vendor/cloud/token-vendor-policy.yaml",
"chars": 601,
"preview": "apiVersion: v1\nkind: ServiceAccount\nmetadata:\n name: token-vendor\n annotations:\n iam.gke.io/gcp-service-account: \"t"
},
{
"path": "src/app_charts/token-vendor/cloud/token-vendor.yaml",
"chars": 1900,
"preview": "apiVersion: apps/v1\nkind: Deployment\nmetadata:\n name: token-vendor\nspec:\n replicas: 1\n selector:\n matchLabels:\n "
},
{
"path": "src/app_charts/token-vendor/dashboard.json",
"chars": 10593,
"preview": "{\n \"annotations\": {\n \"list\": [\n {\n \"builtIn\": 1,\n \"datasource\": {\n \"type\": \"grafana\",\n "
},
{
"path": "src/bootstrap/cloud/BUILD.bazel",
"chars": 1551,
"preview": "load(\"@rules_pkg//pkg:tar.bzl\", \"pkg_tar\")\n\ngenrule(\n name = \"setup-robot-digest\",\n srcs = [\"//src/go/cmd/setup-ro"
},
{
"path": "src/bootstrap/cloud/INSTALL_FROM_BINARY",
"chars": 119,
"preview": "# When this marker file is present, the deploy.sh script installs the binary instead of building\n# from local sources.\n"
},
{
"path": "src/bootstrap/cloud/run-install.sh",
"chars": 2751,
"preview": "#!/bin/bash\n#\n# Copyright 2019 The Cloud Robotics Authors\n#\n# Licensed under the Apache License, Version 2.0 (the \"Licen"
},
{
"path": "src/bootstrap/cloud/terraform/.gitignore",
"chars": 85,
"preview": ".terraform\nconfig.auto.tfvars\nterraform.tfvars\nbackend.tf\nterraform.tfstate.*.backup\n"
},
{
"path": "src/bootstrap/cloud/terraform/BUILD.bazel",
"chars": 195,
"preview": "filegroup(\n name = \"terraform\",\n srcs = [\n \"www.yaml\",\n ] + glob(\n include = [\"*.tf\"],\n ex"
},
{
"path": "src/bootstrap/cloud/terraform/README.md",
"chars": 1160,
"preview": "# Terraform\n\nThese files can be used to create, update or delete your personal development\nor shared projects.\n\nTo updat"
}
]
// ... and 262 more files (download for full content)
About this extraction
This page contains the full source code of the googlecloudrobotics/core GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 462 files (3.5 MB), approximately 951.7k tokens, and a symbol index with 1320 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.