From b839fd052e19aa80d538190a532ebb206ed527ad Mon Sep 17 00:00:00 2001
From: Vaibhav
Date: Wed, 11 Dec 2019 21:04:48 -0500
Subject: [PATCH] Release 0.12 (#423)
---
.argo-ci/ci.yaml | 120 +-
.travis.yml | 6 +-
Gopkg.lock | 480 ++-
Gopkg.toml | 6 +-
Makefile | 94 +-
README.md | 29 +-
VERSION | 2 +-
api/event-source.html | 1755 ++++++++
api/event-source.md | 3489 ++++++++++++++++
api/gateway.html | 731 ++++
api/gateway.md | 1451 +++++++
api/generate.sh | 5 +
api/sensor.html | 1816 ++++++++
api/sensor.md | 3661 +++++++++++++++++
common/common.go | 154 +-
common/logger.go | 31 +-
common/util.go | 30 +-
common/util_test.go | 19 +-
controllers/common/informer.go | 86 -
controllers/common/informer_test.go | 191 -
controllers/common/util.go | 58 +-
controllers/common/util_test.go | 66 +-
.../gateway/cmd}/main.go | 8 +-
.../gateway/common.go | 28 +-
controllers/gateway/config.go | 29 +-
controllers/gateway/config_test.go | 65 +-
controllers/gateway/controller.go | 145 +-
controllers/gateway/controller_test.go | 120 +-
controllers/gateway/informer.go | 37 +-
controllers/gateway/informer_test.go | 40 +-
controllers/gateway/operator.go | 408 +-
controllers/gateway/operator_test.go | 450 +-
controllers/gateway/resource.go | 309 +-
controllers/gateway/resource_test.go | 316 ++
controllers/gateway/state.go | 50 -
controllers/gateway/state_test.go | 44 -
controllers/gateway/validate.go | 50 +-
controllers/gateway/validate_test.go | 31 +-
.../sensor => controllers/sensor/cmd}/main.go | 10 +-
.../sensor/common.go | 35 +-
controllers/sensor/config.go | 57 +-
controllers/sensor/config_test.go | 60 +-
controllers/sensor/controller.go | 193 +-
controllers/sensor/controller_test.go | 118 +-
controllers/sensor/informer.go | 48 +-
controllers/sensor/informer_test.go | 28 +-
controllers/sensor/{state.go => node.go} | 53 +-
controllers/sensor/node_test.go | 93 +
controllers/sensor/operator.go | 485 +--
controllers/sensor/operator_test.go | 462 +--
controllers/sensor/resource.go | 270 +-
controllers/sensor/resource_test.go | 170 +
controllers/sensor/state_test.go | 65 -
controllers/sensor/validate.go | 1 -
controllers/sensor/validate_test.go | 28 +-
docs/assets/argo.png | Bin 0 -> 27999 bytes
docs/assets/gateway.png | Bin 0 -> 97755 bytes
docs/assets/gateways.png | Bin 120284 -> 0 bytes
docs/assets/sensor.png | Bin 82058 -> 86958 bytes
docs/communication.md | 55 -
docs/concepts/event_source.md | 7 +
docs/concepts/gateway.md | 20 +
docs/{ => concepts}/parameterization.md | 0
docs/concepts/sensor.md | 16 +
docs/{ => concepts}/trigger.md | 5 +-
docs/controllers.md | 24 +-
docs/developer_guide.md | 89 +
docs/gateway.md | 179 -
docs/gateways/artifact.md | 32 -
docs/gateways/aws-sns.md | 26 -
docs/gateways/aws-sqs.md | 25 -
docs/gateways/calendar.md | 11 -
docs/gateways/file.md | 22 -
docs/gateways/gcp-pubsub.md | 15 -
docs/gateways/github.md | 24 -
docs/gateways/gitlab.md | 21 -
docs/gateways/resource.md | 11 -
docs/gateways/slack.md | 15 -
docs/gateways/storage-grid.md | 46 -
docs/gateways/streams.md | 20 -
docs/gateways/webhook.md | 35 -
docs/getting_started.md | 29 +-
docs/index.md | 34 +-
docs/installation.md | 25 +-
docs/sensor.md | 453 --
examples/event-sources/amqp.yaml | 62 +-
examples/event-sources/artifact.yaml | 61 -
examples/event-sources/aws-sns.yaml | 124 +-
examples/event-sources/aws-sqs.yaml | 75 +-
examples/event-sources/calendar.yaml | 71 +-
examples/event-sources/file.yaml | 44 +-
examples/event-sources/gcp-pubsub.yaml | 34 +-
examples/event-sources/github.yaml | 138 +-
examples/event-sources/gitlab.yaml | 100 +-
examples/event-sources/hdfs.yaml | 56 +-
examples/event-sources/kafka.yaml | 56 +-
examples/event-sources/minio.yaml | 57 +
examples/event-sources/mqtt.yaml | 58 +-
examples/event-sources/nats.yaml | 54 +-
examples/event-sources/resource.yaml | 144 +-
examples/event-sources/slack.yaml | 96 +-
examples/event-sources/storage-grid.yaml | 66 +-
examples/event-sources/webhook.yaml | 60 +-
examples/gateways/amqp.yaml | 12 +-
examples/gateways/aws-sns.yaml | 11 +-
examples/gateways/aws-sqs.yaml | 11 +-
examples/gateways/calendar.yaml | 9 +-
examples/gateways/file.yaml | 9 +-
examples/gateways/gcp-pubsub.yaml | 9 +-
examples/gateways/github.yaml | 9 +-
examples/gateways/gitlab.yaml | 9 +-
examples/gateways/hdfs.yaml | 27 +-
examples/gateways/kafka.yaml | 9 +-
...standard.yaml => minio-nats-standard.yaml} | 23 +-
...reaming.yaml => minio-nats-streaming.yaml} | 23 +-
.../gateways/{artifact.yaml => minio.yaml} | 26 +-
examples/gateways/mqtt.yaml | 9 +-
examples/gateways/multi-watchers.yaml | 21 +-
examples/gateways/nats.yaml | 9 +-
examples/gateways/resource.yaml | 9 +-
examples/gateways/secure-webhook.yaml | 9 +-
.../sensor-in-different-namespace.yaml | 9 +-
examples/gateways/slack.yaml | 9 +-
examples/gateways/storage-grid.yaml | 11 +-
examples/gateways/webhook-nats-standard.yaml | 9 +-
examples/gateways/webhook-nats-streaming.yaml | 9 +-
examples/gateways/webhook.yaml | 31 +-
examples/sensors/amqp.yaml | 27 +-
.../artifact-with-param-nats-standard.yaml | 51 -
.../artifact-with-param-nats-streaming.yaml | 53 -
examples/sensors/aws-sns.yaml | 6 +-
examples/sensors/aws-sqs.yaml | 10 +-
examples/sensors/calendar.yaml | 5 +-
.../complete-trigger-parameterization.yaml | 3 -
examples/sensors/context-filter-webhook.yaml | 3 -
examples/sensors/data-filter-webhook.yaml | 9 +-
.../sensors/dependencies-circuit-complex.yaml | 148 -
examples/sensors/dependencies-circuit.yaml | 17 +-
examples/sensors/file.yaml | 5 +-
examples/sensors/gcp-pubsub.yaml | 9 +-
examples/sensors/github.yaml | 5 +-
examples/sensors/gitlab.yaml | 7 +-
examples/sensors/hdfs.yaml | 37 +-
examples/sensors/kafka.yaml | 5 +-
.../sensors/{artifact.yaml => minio.yaml} | 15 +-
examples/sensors/mqtt-sensor.yaml | 5 +-
examples/sensors/multi-signal-sensor.yaml | 11 +-
examples/sensors/multi-trigger-sensor.yaml | 35 +-
examples/sensors/nats.yaml | 5 +-
examples/sensors/resource.yaml | 5 +-
examples/sensors/slack.yaml | 9 +-
examples/sensors/storage-grid.yaml | 5 +-
examples/sensors/time-filter-webhook.yaml | 35 +-
examples/sensors/trigger-gateway.yaml | 96 -
examples/sensors/trigger-resource.yaml | 54 -
.../sensors/trigger-source-configmap.yaml | 7 +-
examples/sensors/trigger-source-file.yaml | 3 -
examples/sensors/trigger-source-git.yaml | 5 +-
.../trigger-standard-k8s-resource.yaml | 9 +-
examples/sensors/trigger-with-backoff.yaml | 13 +-
examples/sensors/url-sensor.yaml | 5 +-
examples/sensors/webhook-nats-streaming.yaml | 5 +-
examples/sensors/webhook-nats.yaml | 5 +-
examples/sensors/webhook.yaml | 5 +-
gateways/{ => client}/Dockerfile | 0
gateways/{cmd/main.go => client/client.go} | 15 +-
gateways/client/context.go | 158 +
gateways/client/event-source_test.go | 240 ++
gateways/client/event-sources.go | 356 ++
gateways/{ => client}/state.go | 95 +-
gateways/{ => client}/state_test.go | 44 +-
gateways/client/transformer.go | 165 +
gateways/{ => client}/transformer_test.go | 22 +-
gateways/{ => client}/watcher.go | 91 +-
gateways/common.go | 14 +
gateways/common/fake.go | 100 -
gateways/common/validate.go | 47 -
gateways/common/webhook.go | 314 --
gateways/common/webhook_test.go | 151 -
gateways/community/aws-sns/config_test.go | 65 -
gateways/community/aws-sns/start.go | 188 -
gateways/community/aws-sns/start_test.go | 120 -
gateways/community/aws-sns/validate.go | 44 -
gateways/community/aws-sns/validate_test.go | 58 -
gateways/community/aws-sqs/config.go | 63 -
gateways/community/aws-sqs/config_test.go | 59 -
gateways/community/aws-sqs/start.go | 117 -
gateways/community/aws-sqs/start_test.go | 90 -
gateways/community/aws-sqs/validate.go | 47 -
gateways/community/aws-sqs/validate_test.go | 58 -
gateways/community/gcp-pubsub/config.go | 51 -
gateways/community/gcp-pubsub/start.go | 128 -
gateways/community/gcp-pubsub/start_test.go | 57 -
gateways/community/gcp-pubsub/validate.go | 50 -
.../community/gcp-pubsub/validate_test.go | 59 -
gateways/community/github/config.go | 90 -
gateways/community/github/config_test.go | 47 -
gateways/community/github/start.go | 247 --
gateways/community/github/validate.go | 51 -
gateways/community/github/validate_test.go | 58 -
gateways/community/gitlab/cmd/main.go | 44 -
gateways/community/gitlab/config.go | 87 -
gateways/community/gitlab/config_test.go | 48 -
gateways/community/gitlab/start.go | 157 -
gateways/community/gitlab/start_test.go | 111 -
gateways/community/gitlab/validate.go | 47 -
gateways/community/gitlab/validate_test.go | 59 -
gateways/community/hdfs/cmd/main.go | 28 -
gateways/community/hdfs/config.go | 99 -
gateways/community/hdfs/start.go | 154 -
gateways/community/hdfs/validate.go | 59 -
gateways/community/hdfs/validate_test.go | 42 -
gateways/community/slack/config.go | 63 -
gateways/community/slack/validate.go | 41 -
gateways/community/slack/validate_test.go | 58 -
gateways/community/storagegrid/cmd/main.go | 29 -
gateways/community/storagegrid/config_test.go | 44 -
gateways/community/storagegrid/start.go | 183 -
gateways/community/storagegrid/validate.go | 36 -
.../community/storagegrid/validate_test.go | 58 -
gateways/config.go | 168 -
gateways/core/artifact/Dockerfile | 3 -
gateways/core/artifact/config.go | 44 -
gateways/core/artifact/config_test.go | 51 -
gateways/core/artifact/start.go | 88 -
gateways/core/artifact/validate.go | 60 -
gateways/core/artifact/validate_test.go | 58 -
gateways/core/calendar/config.go | 77 -
gateways/core/calendar/config_test.go | 37 -
gateways/core/calendar/start.go | 143 -
gateways/core/calendar/validate.go | 44 -
gateways/core/calendar/validate_test.go | 58 -
gateways/core/file/config.go | 49 -
gateways/core/file/config_test.go | 38 -
gateways/core/file/start.go | 122 -
gateways/core/file/validate.go | 42 -
gateways/core/file/validate_test.go | 58 -
gateways/core/resource/config.go | 79 -
gateways/core/resource/config_test.go | 49 -
gateways/core/resource/validate.go | 44 -
gateways/core/resource/validate_test.go | 59 -
gateways/core/stream/amqp/config.go | 71 -
gateways/core/stream/amqp/config_test.go | 39 -
gateways/core/stream/amqp/start.go | 148 -
gateways/core/stream/amqp/validate.go | 49 -
gateways/core/stream/amqp/validate_test.go | 58 -
gateways/core/stream/kafka/config.go | 54 -
gateways/core/stream/kafka/config_test.go | 38 -
gateways/core/stream/kafka/start.go | 121 -
gateways/core/stream/kafka/validate.go | 47 -
gateways/core/stream/kafka/validate_test.go | 58 -
gateways/core/stream/mqtt/config.go | 54 -
gateways/core/stream/mqtt/start.go | 91 -
gateways/core/stream/mqtt/validate.go | 46 -
gateways/core/stream/mqtt/validate_test.go | 58 -
gateways/core/stream/nats/config.go | 52 -
gateways/core/stream/nats/config_test.go | 37 -
gateways/core/stream/nats/start.go | 90 -
gateways/core/stream/nats/validate.go | 43 -
gateways/core/stream/nats/validate_test.go | 58 -
gateways/core/webhook/cmd/main.go | 29 -
gateways/core/webhook/config.go | 43 -
gateways/core/webhook/start.go | 120 -
gateways/core/webhook/start_test.go | 74 -
gateways/core/webhook/validate.go | 51 -
gateways/core/webhook/validate_test.go | 72 -
gateways/event-source_test.go | 170 -
gateways/event-sources.go | 275 --
gateways/eventing.pb.go | 62 +-
gateways/eventing.proto | 10 +-
.../{core/stream => server}/amqp/Dockerfile | 0
.../{core/file => server/amqp}/cmd/main.go | 8 +-
gateways/server/amqp/start.go | 135 +
gateways/server/amqp/validate.go | 78 +
gateways/server/amqp/validate_test.go | 64 +
.../{community => server}/aws-sns/Dockerfile | 0
.../{community => server}/aws-sns/cmd/main.go | 16 +-
gateways/server/aws-sns/start.go | 187 +
.../config.go => server/aws-sns/types.go} | 59 +-
gateways/server/aws-sns/validate.go | 71 +
gateways/server/aws-sns/validate_test.go | 64 +
.../{community => server}/aws-sqs/Dockerfile | 0
.../artifact => server/aws-sqs}/cmd/main.go | 14 +-
gateways/server/aws-sqs/start.go | 121 +
gateways/server/aws-sqs/validate.go | 73 +
gateways/server/aws-sqs/validate_test.go | 64 +
gateways/{core => server}/calendar/Dockerfile | 0
gateways/server/calendar/cmd/main.go | 29 +
gateways/server/calendar/start.go | 164 +
.../{core => server}/calendar/start_test.go | 47 +-
gateways/server/calendar/validate.go | 74 +
gateways/server/calendar/validate_test.go | 64 +
gateways/{common => server/common/aws}/aws.go | 16 +-
.../{common => server/common/aws}/aws_test.go | 2 +-
gateways/server/common/fake.go | 41 +
.../common/fsevent}/config.go | 2 +-
.../common/fsevent}/config_test.go | 2 +-
.../{ => server}/common/fsevent/fileevent.go | 0
.../{ => server}/common/naivewatcher/mutex.go | 0
.../common/naivewatcher/watcher.go | 2 +-
.../common/naivewatcher/watcher_test.go | 2 +-
gateways/server/common/webhook/fake.go | 76 +
gateways/server/common/webhook/types.go | 92 +
gateways/server/common/webhook/validate.go | 62 +
gateways/server/common/webhook/webhook.go | 193 +
.../common/webhook/webhook_test.go} | 28 +-
gateways/{core => server}/file/Dockerfile | 0
.../stream/nats => server/file}/cmd/main.go | 8 +-
gateways/server/file/start.go | 141 +
gateways/server/file/validate.go | 68 +
gateways/server/file/validate_test.go | 64 +
.../gcp-pubsub/Dockerfile | 0
.../gcp-pubsub}/cmd/main.go | 8 +-
gateways/server/gcp-pubsub/start.go | 156 +
gateways/server/gcp-pubsub/validate.go | 76 +
gateways/server/gcp-pubsub/validate_test.go | 64 +
.../{community => server}/github/Dockerfile | 0
.../{community => server}/github/cmd/main.go | 15 +-
.../{community => server}/github/hook_util.go | 0
.../github/hook_util_test.go | 4 +-
gateways/server/github/start.go | 299 ++
.../github/start_test.go | 92 +-
.../{community => server}/github/tokenauth.go | 0
gateways/server/github/types.go | 54 +
gateways/server/github/validate.go | 79 +
gateways/server/github/validate_test.go | 64 +
.../{community => server}/gitlab/Dockerfile | 0
.../aws-sqs => server/gitlab}/cmd/main.go | 15 +-
gateways/server/gitlab/start.go | 202 +
gateways/server/gitlab/types.go | 54 +
gateways/server/gitlab/validate.go | 76 +
gateways/server/gitlab/validate_test.go | 66 +
.../{community => server}/hdfs/Dockerfile | 0
gateways/{community => server}/hdfs/client.go | 27 +-
gateways/server/hdfs/cmd/main.go | 23 +
gateways/server/hdfs/start.go | 182 +
gateways/server/hdfs/validate.go | 101 +
gateways/server/hdfs/validate_test.go | 50 +
.../{core/stream => server}/kafka/Dockerfile | 0
.../stream/amqp => server/kafka}/cmd/main.go | 8 +-
gateways/server/kafka/start.go | 143 +
gateways/server/kafka/validate.go | 75 +
gateways/server/kafka/validate_test.go | 64 +
gateways/server/minio/Dockerfile | 3 +
.../slack => server/minio}/cmd/main.go | 16 +-
gateways/server/minio/start.go | 108 +
.../artifact => server/minio}/start_test.go | 53 +-
gateways/server/minio/validate.go | 84 +
gateways/server/minio/validate_test.go | 66 +
.../{core/stream => server}/mqtt/Dockerfile | 0
.../{core/stream => server}/mqtt/cmd/main.go | 8 +-
gateways/server/mqtt/start.go | 110 +
gateways/server/mqtt/validate.go | 75 +
gateways/server/mqtt/validate_test.go | 64 +
.../{core/stream => server}/nats/Dockerfile | 0
gateways/server/nats/cmd/main.go | 29 +
gateways/server/nats/start.go | 109 +
gateways/server/nats/validate.go | 72 +
gateways/server/nats/validate_test.go | 64 +
gateways/{core => server}/resource/Dockerfile | 0
.../{core => server}/resource/cmd/main.go | 8 +-
gateways/{core => server}/resource/start.go | 88 +-
.../{core => server}/resource/start_test.go | 24 +-
gateways/server/resource/validate.go | 72 +
gateways/server/resource/validate_test.go | 66 +
gateways/{gateway.go => server/server.go} | 20 +-
.../server_test.go} | 12 +-
.../{community => server}/slack/Dockerfile | 0
gateways/server/slack/cmd/main.go | 39 +
gateways/{community => server}/slack/start.go | 143 +-
.../{community => server}/slack/start_test.go | 97 +-
gateways/server/slack/types.go | 46 +
gateways/server/slack/validate.go | 70 +
gateways/server/slack/validate_test.go | 64 +
.../storagegrid/Dockerfile | 0
.../kafka => server/storagegrid}/cmd/main.go | 8 +-
gateways/server/storagegrid/start.go | 199 +
.../storagegrid/start_test.go | 93 +-
.../config.go => server/storagegrid/types.go} | 61 +-
gateways/server/storagegrid/validate.go | 66 +
gateways/server/storagegrid/validate_test.go | 66 +
gateways/{ => server}/utils.go | 2 +-
gateways/{ => server}/utils_test.go | 2 +-
gateways/{core => server}/webhook/Dockerfile | 0
gateways/server/webhook/cmd/main.go | 29 +
gateways/server/webhook/start.go | 145 +
gateways/server/webhook/validate.go | 74 +
gateways/server/webhook/validate_test.go | 64 +
gateways/transformer.go | 175 -
.../gateway-controller-deployment.yaml | 20 +-
.../sensor-controller-deployment.yaml | 20 +-
.../manifests/argo-events-cluster-roles.yaml | 10 +-
hack/k8s/manifests/argo-events-role.yaml | 82 +
hack/k8s/manifests/event-source-crd.yaml | 16 +
.../gateway-controller-deployment.yaml | 20 +-
hack/k8s/manifests/gateway-crd.yaml | 4 +-
hack/k8s/manifests/installation.yaml | 217 +
.../sensor-controller-deployment.yaml | 21 +-
hack/k8s/manifests/sensor-crd.yaml | 4 +-
hack/k8s/manifests/workflow-crd.yaml | 12 -
hack/update-api-docs.sh | 34 +
hack/update-codegen.sh | 20 +-
hack/update-openapigen.sh | 7 +
mkdocs.yml | 14 +-
pkg/apis/common/deepcopy_generated.go | 18 -
pkg/apis/common/event-sources.go | 41 +
pkg/apis/common/event.go | 20 +-
pkg/apis/common/s3.go | 2 +-
.../apis/eventsources/register.go | 23 +-
pkg/apis/eventsources/v1alpha1/doc.go | 21 +
.../v1alpha1/openapi_generated.go | 1391 +++++++
pkg/apis/eventsources/v1alpha1/register.go | 56 +
pkg/apis/eventsources/v1alpha1/types.go | 387 ++
.../apis/eventsources/v1alpha1/validate.go | 20 +-
.../v1alpha1/zz_generated.deepcopy.go | 681 +++
.../gateway/v1alpha1/openapi_generated.go | 90 +-
pkg/apis/gateway/v1alpha1/types.go | 58 +-
.../gateway/v1alpha1/zz_generated.deepcopy.go | 63 +-
pkg/apis/sensor/v1alpha1/openapi_generated.go | 61 +-
pkg/apis/sensor/v1alpha1/types.go | 104 +-
.../sensor/v1alpha1/zz_generated.deepcopy.go | 35 +-
.../clientset/versioned/clientset.go | 89 +
.../eventsources/clientset/versioned/doc.go | 19 +
.../versioned/fake/clientset_generated.go | 81 +
.../clientset/versioned/fake/doc.go | 19 +
.../clientset/versioned/fake/register.go | 55 +
.../clientset/versioned/scheme/doc.go | 19 +
.../clientset/versioned/scheme/register.go | 55 +
.../typed/eventsources/v1alpha1/doc.go | 19 +
.../eventsources/v1alpha1/eventsource.go | 190 +
.../v1alpha1/eventsources_client.go | 88 +
.../typed/eventsources/v1alpha1/fake/doc.go | 19 +
.../v1alpha1/fake/fake_eventsource.go | 139 +
.../v1alpha1/fake/fake_eventsources_client.go | 39 +
.../v1alpha1/generated_expansion.go | 8 +-
.../eventsources/interface.go | 45 +
.../eventsources/v1alpha1/eventsource.go | 88 +
.../eventsources/v1alpha1/interface.go | 44 +
.../informers/externalversions/factory.go | 179 +
.../informers/externalversions/generic.go | 61 +
.../internalinterfaces/factory_interfaces.go | 39 +
.../eventsources/v1alpha1/eventsource.go | 93 +
.../v1alpha1/expansion_generated.go | 28 +-
.../gateway/clientset/versioned/clientset.go | 6 +-
.../clientset/versioned/fake/register.go | 6 +-
.../clientset/versioned/scheme/register.go | 6 +-
.../gateway/v1alpha1/fake/fake_gateway.go | 12 +-
.../typed/gateway/v1alpha1/gateway.go | 10 +-
.../typed/gateway/v1alpha1/gateway_client.go | 4 +-
.../informers/externalversions/factory.go | 16 +-
.../internalinterfaces/factory_interfaces.go | 8 +-
.../sensor/clientset/versioned/clientset.go | 6 +-
.../clientset/versioned/fake/register.go | 6 +-
.../clientset/versioned/scheme/register.go | 6 +-
.../typed/sensor/v1alpha1/fake/fake_sensor.go | 12 +-
.../versioned/typed/sensor/v1alpha1/sensor.go | 10 +-
.../typed/sensor/v1alpha1/sensor_client.go | 4 +-
.../informers/externalversions/factory.go | 16 +-
.../internalinterfaces/factory_interfaces.go | 8 +-
sensors/cmd/client.go | 2 +-
sensors/event-handler.go | 12 +-
sensors/event-handler_test.go | 475 ---
sensors/signal-filter_test.go | 343 --
sensors/trigger-params_test.go | 229 --
sensors/trigger.go | 11 +-
sensors/trigger_test.go | 457 --
store/configmap_test.go | 2 +-
store/creds.go | 4 +-
store/creds_test.go | 4 +-
store/git_test.go | 4 +-
store/resource.go | 2 +-
store/store_test.go | 94 +-
test/e2e/common/client.go | 129 -
test/e2e/core/main_test.go | 165 -
.../webhook-gateway-event-source.yaml | 15 -
.../general-use-case/webhook-gateway.yaml | 56 -
.../general-use-case/webhook-sensor.yaml | 45 -
version.go | 2 +-
478 files changed, 28737 insertions(+), 15890 deletions(-)
create mode 100644 api/event-source.html
create mode 100644 api/event-source.md
create mode 100644 api/gateway.html
create mode 100644 api/gateway.md
create mode 100644 api/generate.sh
create mode 100644 api/sensor.html
create mode 100644 api/sensor.md
delete mode 100644 controllers/common/informer.go
delete mode 100644 controllers/common/informer_test.go
rename {cmd/controllers/gateway => controllers/gateway/cmd}/main.go (82%)
rename gateways/community/gcp-pubsub/config_test.go => controllers/gateway/common.go (54%)
create mode 100644 controllers/gateway/resource_test.go
delete mode 100644 controllers/gateway/state.go
delete mode 100644 controllers/gateway/state_test.go
rename {cmd/controllers/sensor => controllers/sensor/cmd}/main.go (78%)
rename gateways/community/slack/config_test.go => controllers/sensor/common.go (50%)
rename controllers/sensor/{state.go => node.go} (65%)
create mode 100644 controllers/sensor/node_test.go
create mode 100644 controllers/sensor/resource_test.go
delete mode 100644 controllers/sensor/state_test.go
create mode 100644 docs/assets/argo.png
create mode 100644 docs/assets/gateway.png
delete mode 100644 docs/assets/gateways.png
delete mode 100644 docs/communication.md
create mode 100644 docs/concepts/event_source.md
create mode 100644 docs/concepts/gateway.md
rename docs/{ => concepts}/parameterization.md (100%)
create mode 100644 docs/concepts/sensor.md
rename docs/{ => concepts}/trigger.md (95%)
create mode 100644 docs/developer_guide.md
delete mode 100644 docs/gateway.md
delete mode 100644 docs/gateways/artifact.md
delete mode 100644 docs/gateways/aws-sns.md
delete mode 100644 docs/gateways/aws-sqs.md
delete mode 100644 docs/gateways/calendar.md
delete mode 100644 docs/gateways/file.md
delete mode 100644 docs/gateways/gcp-pubsub.md
delete mode 100644 docs/gateways/github.md
delete mode 100644 docs/gateways/gitlab.md
delete mode 100644 docs/gateways/resource.md
delete mode 100644 docs/gateways/slack.md
delete mode 100644 docs/gateways/storage-grid.md
delete mode 100644 docs/gateways/streams.md
delete mode 100644 docs/gateways/webhook.md
delete mode 100644 docs/sensor.md
delete mode 100644 examples/event-sources/artifact.yaml
create mode 100644 examples/event-sources/minio.yaml
rename examples/gateways/{artifact-nats-standard.yaml => minio-nats-standard.yaml} (59%)
rename examples/gateways/{artifact-nats-streaming.yaml => minio-nats-streaming.yaml} (61%)
rename examples/gateways/{artifact.yaml => minio.yaml} (59%)
delete mode 100644 examples/sensors/artifact-with-param-nats-standard.yaml
delete mode 100644 examples/sensors/artifact-with-param-nats-streaming.yaml
delete mode 100644 examples/sensors/dependencies-circuit-complex.yaml
rename examples/sensors/{artifact.yaml => minio.yaml} (77%)
delete mode 100644 examples/sensors/trigger-gateway.yaml
delete mode 100644 examples/sensors/trigger-resource.yaml
rename gateways/{ => client}/Dockerfile (100%)
rename gateways/{cmd/main.go => client/client.go} (78%)
create mode 100644 gateways/client/context.go
create mode 100644 gateways/client/event-source_test.go
create mode 100644 gateways/client/event-sources.go
rename gateways/{ => client}/state.go (50%)
rename gateways/{ => client}/state_test.go (75%)
create mode 100644 gateways/client/transformer.go
rename gateways/{ => client}/transformer_test.go (82%)
rename gateways/{ => client}/watcher.go (54%)
create mode 100644 gateways/common.go
delete mode 100644 gateways/common/fake.go
delete mode 100644 gateways/common/validate.go
delete mode 100644 gateways/common/webhook.go
delete mode 100644 gateways/common/webhook_test.go
delete mode 100644 gateways/community/aws-sns/config_test.go
delete mode 100644 gateways/community/aws-sns/start.go
delete mode 100644 gateways/community/aws-sns/start_test.go
delete mode 100644 gateways/community/aws-sns/validate.go
delete mode 100644 gateways/community/aws-sns/validate_test.go
delete mode 100644 gateways/community/aws-sqs/config.go
delete mode 100644 gateways/community/aws-sqs/config_test.go
delete mode 100644 gateways/community/aws-sqs/start.go
delete mode 100644 gateways/community/aws-sqs/start_test.go
delete mode 100644 gateways/community/aws-sqs/validate.go
delete mode 100644 gateways/community/aws-sqs/validate_test.go
delete mode 100644 gateways/community/gcp-pubsub/config.go
delete mode 100644 gateways/community/gcp-pubsub/start.go
delete mode 100644 gateways/community/gcp-pubsub/start_test.go
delete mode 100644 gateways/community/gcp-pubsub/validate.go
delete mode 100644 gateways/community/gcp-pubsub/validate_test.go
delete mode 100644 gateways/community/github/config.go
delete mode 100644 gateways/community/github/config_test.go
delete mode 100644 gateways/community/github/start.go
delete mode 100644 gateways/community/github/validate.go
delete mode 100644 gateways/community/github/validate_test.go
delete mode 100644 gateways/community/gitlab/cmd/main.go
delete mode 100644 gateways/community/gitlab/config.go
delete mode 100644 gateways/community/gitlab/config_test.go
delete mode 100644 gateways/community/gitlab/start.go
delete mode 100644 gateways/community/gitlab/start_test.go
delete mode 100644 gateways/community/gitlab/validate.go
delete mode 100644 gateways/community/gitlab/validate_test.go
delete mode 100644 gateways/community/hdfs/cmd/main.go
delete mode 100644 gateways/community/hdfs/config.go
delete mode 100644 gateways/community/hdfs/start.go
delete mode 100644 gateways/community/hdfs/validate.go
delete mode 100644 gateways/community/hdfs/validate_test.go
delete mode 100644 gateways/community/slack/config.go
delete mode 100644 gateways/community/slack/validate.go
delete mode 100644 gateways/community/slack/validate_test.go
delete mode 100644 gateways/community/storagegrid/cmd/main.go
delete mode 100644 gateways/community/storagegrid/config_test.go
delete mode 100644 gateways/community/storagegrid/start.go
delete mode 100644 gateways/community/storagegrid/validate.go
delete mode 100644 gateways/community/storagegrid/validate_test.go
delete mode 100644 gateways/config.go
delete mode 100644 gateways/core/artifact/Dockerfile
delete mode 100644 gateways/core/artifact/config.go
delete mode 100644 gateways/core/artifact/config_test.go
delete mode 100644 gateways/core/artifact/start.go
delete mode 100644 gateways/core/artifact/validate.go
delete mode 100644 gateways/core/artifact/validate_test.go
delete mode 100644 gateways/core/calendar/config.go
delete mode 100644 gateways/core/calendar/config_test.go
delete mode 100644 gateways/core/calendar/start.go
delete mode 100644 gateways/core/calendar/validate.go
delete mode 100644 gateways/core/calendar/validate_test.go
delete mode 100644 gateways/core/file/config.go
delete mode 100644 gateways/core/file/config_test.go
delete mode 100644 gateways/core/file/start.go
delete mode 100644 gateways/core/file/validate.go
delete mode 100644 gateways/core/file/validate_test.go
delete mode 100644 gateways/core/resource/config.go
delete mode 100644 gateways/core/resource/config_test.go
delete mode 100644 gateways/core/resource/validate.go
delete mode 100644 gateways/core/resource/validate_test.go
delete mode 100644 gateways/core/stream/amqp/config.go
delete mode 100644 gateways/core/stream/amqp/config_test.go
delete mode 100644 gateways/core/stream/amqp/start.go
delete mode 100644 gateways/core/stream/amqp/validate.go
delete mode 100644 gateways/core/stream/amqp/validate_test.go
delete mode 100644 gateways/core/stream/kafka/config.go
delete mode 100644 gateways/core/stream/kafka/config_test.go
delete mode 100644 gateways/core/stream/kafka/start.go
delete mode 100644 gateways/core/stream/kafka/validate.go
delete mode 100644 gateways/core/stream/kafka/validate_test.go
delete mode 100644 gateways/core/stream/mqtt/config.go
delete mode 100644 gateways/core/stream/mqtt/start.go
delete mode 100644 gateways/core/stream/mqtt/validate.go
delete mode 100644 gateways/core/stream/mqtt/validate_test.go
delete mode 100644 gateways/core/stream/nats/config.go
delete mode 100644 gateways/core/stream/nats/config_test.go
delete mode 100644 gateways/core/stream/nats/start.go
delete mode 100644 gateways/core/stream/nats/validate.go
delete mode 100644 gateways/core/stream/nats/validate_test.go
delete mode 100644 gateways/core/webhook/cmd/main.go
delete mode 100644 gateways/core/webhook/config.go
delete mode 100644 gateways/core/webhook/start.go
delete mode 100644 gateways/core/webhook/start_test.go
delete mode 100644 gateways/core/webhook/validate.go
delete mode 100644 gateways/core/webhook/validate_test.go
delete mode 100644 gateways/event-source_test.go
delete mode 100644 gateways/event-sources.go
rename gateways/{core/stream => server}/amqp/Dockerfile (100%)
rename gateways/{core/file => server/amqp}/cmd/main.go (77%)
create mode 100644 gateways/server/amqp/start.go
create mode 100644 gateways/server/amqp/validate.go
create mode 100644 gateways/server/amqp/validate_test.go
rename gateways/{community => server}/aws-sns/Dockerfile (100%)
rename gateways/{community => server}/aws-sns/cmd/main.go (70%)
create mode 100644 gateways/server/aws-sns/start.go
rename gateways/{community/aws-sns/config.go => server/aws-sns/types.go} (61%)
create mode 100644 gateways/server/aws-sns/validate.go
create mode 100644 gateways/server/aws-sns/validate_test.go
rename gateways/{community => server}/aws-sqs/Dockerfile (100%)
rename gateways/{core/artifact => server/aws-sqs}/cmd/main.go (76%)
create mode 100644 gateways/server/aws-sqs/start.go
create mode 100644 gateways/server/aws-sqs/validate.go
create mode 100644 gateways/server/aws-sqs/validate_test.go
rename gateways/{core => server}/calendar/Dockerfile (100%)
create mode 100644 gateways/server/calendar/cmd/main.go
create mode 100644 gateways/server/calendar/start.go
rename gateways/{core => server}/calendar/start_test.go (66%)
create mode 100644 gateways/server/calendar/validate.go
create mode 100644 gateways/server/calendar/validate_test.go
rename gateways/{common => server/common/aws}/aws.go (77%)
rename gateways/{common => server/common/aws}/aws_test.go (99%)
create mode 100644 gateways/server/common/fake.go
rename gateways/{common => server/common/fsevent}/config.go (98%)
rename gateways/{common => server/common/fsevent}/config_test.go (98%)
rename gateways/{ => server}/common/fsevent/fileevent.go (100%)
rename gateways/{ => server}/common/naivewatcher/mutex.go (100%)
rename gateways/{ => server}/common/naivewatcher/watcher.go (98%)
rename gateways/{ => server}/common/naivewatcher/watcher_test.go (98%)
create mode 100644 gateways/server/common/webhook/fake.go
create mode 100644 gateways/server/common/webhook/types.go
create mode 100644 gateways/server/common/webhook/validate.go
create mode 100644 gateways/server/common/webhook/webhook.go
rename gateways/{core/webhook/config_test.go => server/common/webhook/webhook_test.go} (61%)
rename gateways/{core => server}/file/Dockerfile (100%)
rename gateways/{core/stream/nats => server/file}/cmd/main.go (76%)
create mode 100644 gateways/server/file/start.go
create mode 100644 gateways/server/file/validate.go
create mode 100644 gateways/server/file/validate_test.go
rename gateways/{community => server}/gcp-pubsub/Dockerfile (100%)
rename gateways/{core/calendar => server/gcp-pubsub}/cmd/main.go (76%)
create mode 100644 gateways/server/gcp-pubsub/start.go
create mode 100644 gateways/server/gcp-pubsub/validate.go
create mode 100644 gateways/server/gcp-pubsub/validate_test.go
rename gateways/{community => server}/github/Dockerfile (100%)
rename gateways/{community => server}/github/cmd/main.go (76%)
rename gateways/{community => server}/github/hook_util.go (100%)
rename gateways/{community => server}/github/hook_util_test.go (100%)
create mode 100644 gateways/server/github/start.go
rename gateways/{community => server}/github/start_test.go (60%)
rename gateways/{community => server}/github/tokenauth.go (100%)
create mode 100644 gateways/server/github/types.go
create mode 100644 gateways/server/github/validate.go
create mode 100644 gateways/server/github/validate_test.go
rename gateways/{community => server}/gitlab/Dockerfile (100%)
rename gateways/{community/aws-sqs => server/gitlab}/cmd/main.go (70%)
create mode 100644 gateways/server/gitlab/start.go
create mode 100644 gateways/server/gitlab/types.go
create mode 100644 gateways/server/gitlab/validate.go
create mode 100644 gateways/server/gitlab/validate_test.go
rename gateways/{community => server}/hdfs/Dockerfile (100%)
rename gateways/{community => server}/hdfs/client.go (79%)
create mode 100644 gateways/server/hdfs/cmd/main.go
create mode 100644 gateways/server/hdfs/start.go
create mode 100644 gateways/server/hdfs/validate.go
create mode 100644 gateways/server/hdfs/validate_test.go
rename gateways/{core/stream => server}/kafka/Dockerfile (100%)
rename gateways/{core/stream/amqp => server/kafka}/cmd/main.go (76%)
create mode 100644 gateways/server/kafka/start.go
create mode 100644 gateways/server/kafka/validate.go
create mode 100644 gateways/server/kafka/validate_test.go
create mode 100644 gateways/server/minio/Dockerfile
rename gateways/{community/slack => server/minio}/cmd/main.go (74%)
create mode 100644 gateways/server/minio/start.go
rename gateways/{core/artifact => server/minio}/start_test.go (59%)
create mode 100644 gateways/server/minio/validate.go
create mode 100644 gateways/server/minio/validate_test.go
rename gateways/{core/stream => server}/mqtt/Dockerfile (100%)
rename gateways/{core/stream => server}/mqtt/cmd/main.go (76%)
create mode 100644 gateways/server/mqtt/start.go
create mode 100644 gateways/server/mqtt/validate.go
create mode 100644 gateways/server/mqtt/validate_test.go
rename gateways/{core/stream => server}/nats/Dockerfile (100%)
create mode 100644 gateways/server/nats/cmd/main.go
create mode 100644 gateways/server/nats/start.go
create mode 100644 gateways/server/nats/validate.go
create mode 100644 gateways/server/nats/validate_test.go
rename gateways/{core => server}/resource/Dockerfile (100%)
rename gateways/{core => server}/resource/cmd/main.go (79%)
rename gateways/{core => server}/resource/start.go (62%)
rename gateways/{core => server}/resource/start_test.go (72%)
create mode 100644 gateways/server/resource/validate.go
create mode 100644 gateways/server/resource/validate_test.go
rename gateways/{gateway.go => server/server.go} (80%)
rename gateways/{gateway_test.go => server/server_test.go} (93%)
rename gateways/{community => server}/slack/Dockerfile (100%)
create mode 100644 gateways/server/slack/cmd/main.go
rename gateways/{community => server}/slack/start.go (54%)
rename gateways/{community => server}/slack/start_test.go (75%)
create mode 100644 gateways/server/slack/types.go
create mode 100644 gateways/server/slack/validate.go
create mode 100644 gateways/server/slack/validate_test.go
rename gateways/{community => server}/storagegrid/Dockerfile (100%)
rename gateways/{core/stream/kafka => server/storagegrid}/cmd/main.go (76%)
create mode 100644 gateways/server/storagegrid/start.go
rename gateways/{community => server}/storagegrid/start_test.go (61%)
rename gateways/{community/storagegrid/config.go => server/storagegrid/types.go} (57%)
create mode 100644 gateways/server/storagegrid/validate.go
create mode 100644 gateways/server/storagegrid/validate_test.go
rename gateways/{ => server}/utils.go (98%)
rename gateways/{ => server}/utils_test.go (98%)
rename gateways/{core => server}/webhook/Dockerfile (100%)
create mode 100644 gateways/server/webhook/cmd/main.go
create mode 100644 gateways/server/webhook/start.go
create mode 100644 gateways/server/webhook/validate.go
create mode 100644 gateways/server/webhook/validate_test.go
delete mode 100644 gateways/transformer.go
create mode 100644 hack/k8s/manifests/argo-events-role.yaml
create mode 100644 hack/k8s/manifests/event-source-crd.yaml
create mode 100644 hack/k8s/manifests/installation.yaml
delete mode 100644 hack/k8s/manifests/workflow-crd.yaml
create mode 100644 hack/update-api-docs.sh
create mode 100644 pkg/apis/common/event-sources.go
rename gateways/common/validate_test.go => pkg/apis/eventsources/register.go (68%)
create mode 100644 pkg/apis/eventsources/v1alpha1/doc.go
create mode 100644 pkg/apis/eventsources/v1alpha1/openapi_generated.go
create mode 100644 pkg/apis/eventsources/v1alpha1/register.go
create mode 100644 pkg/apis/eventsources/v1alpha1/types.go
rename gateways/community/gcp-pubsub/cmd/main.go => pkg/apis/eventsources/v1alpha1/validate.go (62%)
create mode 100644 pkg/apis/eventsources/v1alpha1/zz_generated.deepcopy.go
create mode 100644 pkg/client/eventsources/clientset/versioned/clientset.go
create mode 100644 pkg/client/eventsources/clientset/versioned/doc.go
create mode 100644 pkg/client/eventsources/clientset/versioned/fake/clientset_generated.go
create mode 100644 pkg/client/eventsources/clientset/versioned/fake/doc.go
create mode 100644 pkg/client/eventsources/clientset/versioned/fake/register.go
create mode 100644 pkg/client/eventsources/clientset/versioned/scheme/doc.go
create mode 100644 pkg/client/eventsources/clientset/versioned/scheme/register.go
create mode 100644 pkg/client/eventsources/clientset/versioned/typed/eventsources/v1alpha1/doc.go
create mode 100644 pkg/client/eventsources/clientset/versioned/typed/eventsources/v1alpha1/eventsource.go
create mode 100644 pkg/client/eventsources/clientset/versioned/typed/eventsources/v1alpha1/eventsources_client.go
create mode 100644 pkg/client/eventsources/clientset/versioned/typed/eventsources/v1alpha1/fake/doc.go
create mode 100644 pkg/client/eventsources/clientset/versioned/typed/eventsources/v1alpha1/fake/fake_eventsource.go
create mode 100644 pkg/client/eventsources/clientset/versioned/typed/eventsources/v1alpha1/fake/fake_eventsources_client.go
rename gateways/common/doc.go => pkg/client/eventsources/clientset/versioned/typed/eventsources/v1alpha1/generated_expansion.go (77%)
create mode 100644 pkg/client/eventsources/informers/externalversions/eventsources/interface.go
create mode 100644 pkg/client/eventsources/informers/externalversions/eventsources/v1alpha1/eventsource.go
create mode 100644 pkg/client/eventsources/informers/externalversions/eventsources/v1alpha1/interface.go
create mode 100644 pkg/client/eventsources/informers/externalversions/factory.go
create mode 100644 pkg/client/eventsources/informers/externalversions/generic.go
create mode 100644 pkg/client/eventsources/informers/externalversions/internalinterfaces/factory_interfaces.go
create mode 100644 pkg/client/eventsources/listers/eventsources/v1alpha1/eventsource.go
rename gateways/core/stream/mqtt/config_test.go => pkg/client/eventsources/listers/eventsources/v1alpha1/expansion_generated.go (56%)
delete mode 100644 sensors/event-handler_test.go
delete mode 100644 sensors/signal-filter_test.go
delete mode 100644 sensors/trigger-params_test.go
delete mode 100644 sensors/trigger_test.go
delete mode 100644 test/e2e/common/client.go
delete mode 100644 test/e2e/core/main_test.go
delete mode 100644 test/e2e/core/manifests/general-use-case/webhook-gateway-event-source.yaml
delete mode 100644 test/e2e/core/manifests/general-use-case/webhook-gateway.yaml
delete mode 100644 test/e2e/core/manifests/general-use-case/webhook-sensor.yaml
diff --git a/.argo-ci/ci.yaml b/.argo-ci/ci.yaml
index 1ca24a44e0..8192403390 100644
--- a/.argo-ci/ci.yaml
+++ b/.argo-ci/ci.yaml
@@ -6,68 +6,68 @@ spec:
entrypoint: argo-events-ci
arguments:
parameters:
- - name: revision
- value: master
- - name: repo
- value: https://github.com/argoproj/argo-events.git
+ - name: revision
+ value: master
+ - name: repo
+ value: https://github.com/argoproj/argo-events.git
templates:
- - name: argo-events-ci
- steps:
- - - name: build
- template: ci-dind
- arguments:
- parameters:
+ - name: argo-events-ci
+ steps:
+ - - name: build
+ template: ci-dind
+ arguments:
+ parameters:
+ - name: cmd
+ value: "{{item}}"
+ withItems:
+ - dep ensure && make
+ - name: test
+ template: ci-builder
+ arguments:
+ parameters:
+ - name: cmd
+ value: "{{item}}"
+ withItems:
+ - dep ensure && make test
+ - name: ci-builder
+ inputs:
+ parameters:
- name: cmd
- value: "{{item}}"
- withItems:
- - dep ensure && make
- - name: test
- template: ci-builder
- arguments:
- parameters:
- - name: cmd
- value: "{{item}}"
- withItems:
- - dep ensure && make test
- - name: ci-builder
- inputs:
- parameters:
- - name: cmd
- artifacts:
- - name: code
- path: /go/src/github.com/argoproj/argo-events
- git:
- repo: "{{workflow.parameters.repo}}"
- revision: "{{workflow.parameters.revision}}"
- container:
- image: argoproj/argo-events-ci-builder:1.0
- command: [sh, -c]
- args: ["{{inputs.parameters.cmd}}"]
- workingDir: /go/src/github.com/argoproj/argo-events
+ artifacts:
+ - name: code
+ path: /go/src/github.com/argoproj/argo-events
+ git:
+ repo: "{{workflow.parameters.repo}}"
+ revision: "{{workflow.parameters.revision}}"
+ container:
+ image: argoproj/argo-events-ci-builder:1.0
+ command: [sh, -c]
+ args: ["{{inputs.parameters.cmd}}"]
+ workingDir: /go/src/github.com/argoproj/argo-events
- - name: ci-dind
- inputs:
- parameters:
- - name: cmd
- artifacts:
- - name: code
- path: /go/src/github.com/argoproj/argo-events
- git:
- repo: "{{workflow.parameters.repo}}"
- revision: "{{workflow.parameters.revision}}"
- container:
- image: argoproj/argo-events-ci-builder:1.0
- command: [sh, -c]
- args: ["until docker ps; do sleep 3; done && {{inputs.parameters.cmd}}"]
- workingDir: /go/src/github.com/argoproj/argo-events
- env:
- - name: DOCKER_HOST
- value: 127.0.0.1
- sidecars:
- - name: dind
- image: docker:17.10-dind
- securityContext:
- privileged: true
- mirrorVolumeMounts: true
+ - name: ci-dind
+ inputs:
+ parameters:
+ - name: cmd
+ artifacts:
+ - name: code
+ path: /go/src/github.com/argoproj/argo-events
+ git:
+ repo: "{{workflow.parameters.repo}}"
+ revision: "{{workflow.parameters.revision}}"
+ container:
+ image: argoproj/argo-events-ci-builder:1.0
+ command: [sh, -c]
+ args: ["until docker ps; do sleep 3; done && {{inputs.parameters.cmd}}"]
+ workingDir: /go/src/github.com/argoproj/argo-events
+ env:
+ - name: DOCKER_HOST
+ value: 127.0.0.1
+ sidecars:
+ - name: dind
+ image: docker:17.10-dind
+ securityContext:
+ privileged: true
+ mirrorVolumeMounts: true
diff --git a/.travis.yml b/.travis.yml
index 9dc6ec5edc..f71788633f 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -21,9 +21,9 @@ before_install:
- if [[ -d $HOME/docker ]]; then ls $HOME/docker/*.tar.gz | xargs -I {file} sh -c "zcat {file} | docker load"; fi
- go get github.com/mattn/goveralls
- if [ ! -d $HOME/bin/kubectl ]; then
- mkdir -p $HOME/bin;
- curl -o $HOME/bin/kubectl -L https://storage.googleapis.com/kubernetes-release/release/v1.13.4/bin/linux/amd64/kubectl;
- chmod +x $HOME/bin/kubectl;
+ mkdir -p $HOME/bin;
+ curl -o $HOME/bin/kubectl -L https://storage.googleapis.com/kubernetes-release/release/v1.13.4/bin/linux/amd64/kubectl;
+ chmod +x $HOME/bin/kubectl;
fi
before_cache:
diff --git a/Gopkg.lock b/Gopkg.lock
index 4935d93e52..faa890f74c 100644
--- a/Gopkg.lock
+++ b/Gopkg.lock
@@ -3,6 +3,7 @@
[[projects]]
branch = "master"
+ digest = "1:2ff9987255c3f00d7aa928f67977b23def1bf86ab60a7c862579bf2ee29d85bb"
name = "cloud.google.com/go"
packages = [
".",
@@ -12,51 +13,65 @@
"internal/version",
"pubsub",
"pubsub/apiv1",
- "pubsub/internal/distribution"
+ "pubsub/internal/distribution",
]
- revision = "90c61cb6b2d275ce6037ff82aa8cef13996bd861"
+ pruneopts = "UT"
+ revision = "08bca813144c81c4a70e9f73ce42c98599238e54"
[[projects]]
+ digest = "1:9f3b30d9f8e0d7040f729b82dcbc8f0dead820a133b3147ce355fc451f32d761"
name = "github.com/BurntSushi/toml"
packages = ["."]
+ pruneopts = "UT"
revision = "3012a1dbe2e4bd1391d42b32f0577cb7bbc7f005"
version = "v0.3.1"
[[projects]]
branch = "master"
+ digest = "1:dc648facc1e7aac5086f749c84c9b9263345c08161fadd9cf92ae3309c9fcaa6"
name = "github.com/Knetic/govaluate"
packages = ["."]
+ pruneopts = "UT"
revision = "9aa49832a739dcd78a5542ff189fb82c3e423116"
[[projects]]
+ digest = "1:a2682518d905d662d984ef9959984ef87cecb777d379bfa9d9fe40e78069b3e4"
name = "github.com/PuerkitoBio/purell"
packages = ["."]
+ pruneopts = "UT"
revision = "44968752391892e1b0d0b821ee79e9a85fa13049"
version = "v1.1.1"
[[projects]]
branch = "master"
+ digest = "1:c739832d67eb1e9cc478a19cc1a1ccd78df0397bf8a32978b759152e205f644b"
name = "github.com/PuerkitoBio/urlesc"
packages = ["."]
+ pruneopts = "UT"
revision = "de5bf2ad457846296e2031421a34e2568e304e35"
[[projects]]
+ digest = "1:05705adc86e7ba79c48d17cfbe1760e41d227f43e2880345dd7226e0ce67e4be"
name = "github.com/Shopify/sarama"
packages = ["."]
+ pruneopts = "UT"
revision = "675b0b1ff204c259877004140a540d6adf38db17"
version = "v1.24.1"
[[projects]]
+ digest = "1:252cca52c71315b507cfbad0b0d34c6a525c8c2427f0b09e0bf07baa70b39a51"
name = "github.com/argoproj/argo"
packages = [
"pkg/apis/workflow",
- "pkg/apis/workflow/v1alpha1"
+ "pkg/apis/workflow/v1alpha1",
]
+ pruneopts = "UT"
revision = "675c66267f0c916de0f233d8101aa0646acb46d4"
version = "v2.4.2"
[[projects]]
branch = "master"
+ digest = "1:3dc369d4d676d019a541e5bd27829e689cd6218343843cd84fe5477eb3899ca2"
name = "github.com/aws/aws-sdk-go"
packages = [
"aws",
@@ -92,78 +107,98 @@
"service/sns",
"service/sqs",
"service/sts",
- "service/sts/stsiface"
+ "service/sts/stsiface",
]
- revision = "ab596ec53119dade64e6f3cf60dab4b45e613890"
+ pruneopts = "UT"
+ revision = "36d1d7065765e55ac085734aa68be6de7c380b66"
[[projects]]
+ digest = "1:357f4baa5f50bb2a9d9d01600c8dadebf1cb890b59b53a4c810301fc7bf3736c"
name = "github.com/colinmarc/hdfs"
packages = [
".",
"protocol/hadoop_common",
"protocol/hadoop_hdfs",
- "rpc"
+ "rpc",
]
+ pruneopts = "UT"
revision = "48eb8d6c34a97ffc73b406356f0f2e1c569b42a5"
[[projects]]
+ digest = "1:ffe9824d294da03b391f44e1ae8281281b4afc1bdaa9588c9097785e3af10cec"
name = "github.com/davecgh/go-spew"
packages = ["spew"]
+ pruneopts = "UT"
revision = "8991bc29aa16c548c550c7ff78260e27b9ab7c73"
version = "v1.1.1"
[[projects]]
branch = "master"
+ digest = "1:ecdc8e0fe3bc7d549af1c9c36acf3820523b707d6c071b6d0c3860882c6f7b42"
name = "github.com/docker/spdystream"
packages = [
".",
- "spdy"
+ "spdy",
]
+ pruneopts = "UT"
revision = "6480d4af844c189cf5dd913db24ddd339d3a4f85"
[[projects]]
+ digest = "1:6f9339c912bbdda81302633ad7e99a28dfa5a639c864061f1929510a9a64aa74"
name = "github.com/dustin/go-humanize"
packages = ["."]
+ pruneopts = "UT"
revision = "9f541cc9db5d55bce703bd99987c9d5cb8eea45e"
version = "v1.0.0"
[[projects]]
+ digest = "1:1f0c7ab489b407a7f8f9ad16c25a504d28ab461517a971d341388a56156c1bd7"
name = "github.com/eapache/go-resiliency"
packages = ["breaker"]
+ pruneopts = "UT"
revision = "5efd2ed019fd331ec2defc6f3bd98882f1e3e636"
version = "v1.2.0"
[[projects]]
branch = "master"
+ digest = "1:79f16588b5576b1b3cd90e48d2374cc9a1a8776862d28d8fd0f23b0e15534967"
name = "github.com/eapache/go-xerial-snappy"
packages = ["."]
+ pruneopts = "UT"
revision = "776d5712da21bc4762676d614db1d8a64f4238b0"
[[projects]]
+ digest = "1:444b82bfe35c83bbcaf84e310fb81a1f9ece03edfed586483c869e2c046aef69"
name = "github.com/eapache/queue"
packages = ["."]
+ pruneopts = "UT"
revision = "44cc805cf13205b55f69e14bcb69867d1ae92f98"
version = "v1.1.0"
[[projects]]
+ digest = "1:bb89a2542933056fcebc2950bb15ec636e623cc43c96597288aa2009f15b0ce1"
name = "github.com/eclipse/paho.mqtt.golang"
packages = [
".",
- "packets"
+ "packets",
]
+ pruneopts = "UT"
revision = "adca289fdcf8c883800aafa545bc263452290bae"
version = "v1.2.0"
[[projects]]
+ digest = "1:e15b0065da1011473634ffa0dda17731ea1d5f6fb8bb87905216d95fa29eaec0"
name = "github.com/emicklei/go-restful"
packages = [
".",
- "log"
+ "log",
]
+ pruneopts = "UT"
revision = "99f05a26a0a1c71e664ebe6a76d29b2c80333056"
version = "v2.11.1"
[[projects]]
+ digest = "1:b498b36dbb2b306d1c5205ee5236c9e60352be8f9eea9bf08186723a9f75b4f3"
name = "github.com/emirpasic/gods"
packages = [
"containers",
@@ -171,54 +206,70 @@
"lists/arraylist",
"trees",
"trees/binaryheap",
- "utils"
+ "utils",
]
+ pruneopts = "UT"
revision = "1615341f118ae12f353cc8a983f35b584342c9b3"
version = "v1.12.0"
[[projects]]
+ digest = "1:ac425d784b13d49b37a5bbed3ce022677f8f3073b216f05d6adcb9303e27fa0f"
name = "github.com/evanphx/json-patch"
packages = ["."]
+ pruneopts = "UT"
revision = "026c730a0dcc5d11f93f1cf1cc65b01247ea7b6f"
version = "v4.5.0"
[[projects]]
branch = "master"
+ digest = "1:925a2ad8acf10a486cdae4366eaf45847b16d6d7448e654814d8f1d51adeefe4"
name = "github.com/fsnotify/fsnotify"
packages = ["."]
+ pruneopts = "UT"
revision = "4bf2d1fec78374803a39307bfb8d340688f4f28e"
[[projects]]
branch = "master"
+ digest = "1:08188cf7ce7027b22e88cc23da27f17349a0ba7746271a60cbe0a70266c2346f"
name = "github.com/ghodss/yaml"
packages = ["."]
+ pruneopts = "UT"
revision = "25d852aebe32c875e9c044af3eef9c7dc6bc777f"
[[projects]]
+ digest = "1:ed15647db08b6d63666bf9755d337725960c302bbfa5e23754b4b915a4797e42"
name = "github.com/go-openapi/jsonpointer"
packages = ["."]
+ pruneopts = "UT"
revision = "ed123515f087412cd7ef02e49b0b0a5e6a79a360"
version = "v0.19.3"
[[projects]]
+ digest = "1:451fe53c19443c6941be5d4295edc973a3eb16baccb940efee94284024be03b0"
name = "github.com/go-openapi/jsonreference"
packages = ["."]
+ pruneopts = "UT"
revision = "82f31475a8f7a12bc26962f6e26ceade8ea6f66a"
version = "v0.19.3"
[[projects]]
+ digest = "1:55d1c09fc8b3320b6842565249a9e4d0f363bead6f9b8be05c3b47f2c4264eda"
name = "github.com/go-openapi/spec"
packages = ["."]
+ pruneopts = "UT"
revision = "8557d72e4f077c2dbe1e48df09e596b6fb9b7991"
version = "v0.19.4"
[[projects]]
+ digest = "1:43d0f99f53acce97119181dcd592321084690c2d462c57680ccb4472ae084949"
name = "github.com/go-openapi/swag"
packages = ["."]
+ pruneopts = "UT"
revision = "c3d0f7896d589f3babb99eea24bbc7de98108e72"
version = "v0.19.5"
[[projects]]
+ digest = "1:e5e45557e1871c967a6ccaa5b95d1233a2c01ab00615621825d1aca7383dc022"
name = "github.com/gobwas/glob"
packages = [
".",
@@ -228,11 +279,13 @@
"syntax/ast",
"syntax/lexer",
"util/runes",
- "util/strings"
+ "util/strings",
]
+ pruneopts = "UT"
revision = "e7a84e9525fe90abcda167b604e483cc959ad4aa"
[[projects]]
+ digest = "1:b51c4d0071cbb46efd912e9411ce2ec8755cb67bfa1e2a467b704c4a0469924d"
name = "github.com/gogo/protobuf"
packages = [
"gogoproto",
@@ -262,19 +315,23 @@
"protoc-gen-gogofast",
"sortkeys",
"vanity",
- "vanity/command"
+ "vanity/command",
]
+ pruneopts = "UT"
revision = "5628607bb4c51c3157aacc3a50f0ab707582b805"
version = "v1.3.1"
[[projects]]
branch = "master"
+ digest = "1:b7cb6054d3dff43b38ad2e92492f220f57ae6087ee797dca298139776749ace8"
name = "github.com/golang/groupcache"
packages = ["lru"]
+ pruneopts = "UT"
revision = "611e8accdfc92c4187d399e95ce826046d4c8d73"
[[projects]]
branch = "master"
+ digest = "1:3c60e8d6869358bc8f4e9095cedd83abb34a47b81fe32a263389aba1c0e8f09f"
name = "github.com/golang/protobuf"
packages = [
"proto",
@@ -288,183 +345,241 @@
"ptypes/any",
"ptypes/duration",
"ptypes/empty",
- "ptypes/timestamp"
+ "ptypes/timestamp",
]
+ pruneopts = "UT"
revision = "ed6926b37a637426117ccab59282c3839528a700"
[[projects]]
+ digest = "1:e4f5819333ac698d294fe04dbf640f84719658d5c7ce195b10060cc37292ce79"
name = "github.com/golang/snappy"
packages = ["."]
+ pruneopts = "UT"
revision = "2a8bb927dd31d8daada140a5d09578521ce5c36a"
version = "v0.0.1"
[[projects]]
+ digest = "1:1d1cbf539d9ac35eb3148129f96be5537f1a1330cadcc7e3a83b4e72a59672a3"
name = "github.com/google/go-cmp"
packages = [
"cmp",
"cmp/internal/diff",
"cmp/internal/flags",
"cmp/internal/function",
- "cmp/internal/value"
+ "cmp/internal/value",
]
+ pruneopts = "UT"
revision = "2d0692c2e9617365a95b295612ac0d4415ba4627"
version = "v0.3.1"
[[projects]]
+ digest = "1:a848ff8a9a04616f385520da14d031468ad24e4a9a38f84241d92bd045593251"
name = "github.com/google/go-github"
packages = ["github"]
+ pruneopts = "UT"
revision = "50be09d24ee31a2b0868265e76c24b9545a6eb7a"
[[projects]]
+ digest = "1:a63cff6b5d8b95638bfe300385d93b2a6d9d687734b863da8e09dc834510a690"
name = "github.com/google/go-querystring"
packages = ["query"]
+ pruneopts = "UT"
revision = "44c6ddd0a2342c386950e880b658017258da92fc"
version = "v1.0.0"
[[projects]]
+ digest = "1:a6181aca1fd5e27103f9a920876f29ac72854df7345a39f3b01e61c8c94cc8af"
name = "github.com/google/gofuzz"
packages = ["."]
+ pruneopts = "UT"
revision = "f140a6486e521aad38f5917de355cbf147cc0496"
version = "v1.0.0"
[[projects]]
+ digest = "1:582b704bebaa06b48c29b0cec224a6058a09c86883aaddabde889cd1a5f73e1b"
name = "github.com/google/uuid"
packages = ["."]
+ pruneopts = "UT"
revision = "0cd6bf5da1e1c83f8b45653022c74f71af0538a4"
version = "v1.1.1"
[[projects]]
+ digest = "1:766102087520f9d54f2acc72bd6637045900ac735b4a419b128d216f0c5c4876"
name = "github.com/googleapis/gax-go"
packages = ["v2"]
+ pruneopts = "UT"
revision = "bd5b16380fd03dc758d11cef74ba2e3bc8b0e8c2"
version = "v2.0.5"
[[projects]]
+ digest = "1:ca4524b4855ded427c7003ec903a5c854f37e7b1e8e2a93277243462c5b753a8"
name = "github.com/googleapis/gnostic"
packages = [
"OpenAPIv2",
"compiler",
- "extensions"
+ "extensions",
]
+ pruneopts = "UT"
revision = "ab0dd09aa10e2952b28e12ecd35681b20463ebab"
version = "v0.3.1"
[[projects]]
branch = "master"
+ digest = "1:f14d1b50e0075fb00177f12a96dd7addf93d1e2883c25befd17285b779549795"
name = "github.com/gopherjs/gopherjs"
packages = ["js"]
- revision = "ce3c9ade29deed38a85f259f40e823cc17213830"
+ pruneopts = "UT"
+ revision = "d3ddacdb130fcd23f77a827e3b599804730be6b5"
[[projects]]
+ digest = "1:cbec35fe4d5a4fba369a656a8cd65e244ea2c743007d8f6c1ccb132acf9d1296"
+ name = "github.com/gorilla/mux"
+ packages = ["."]
+ pruneopts = "UT"
+ revision = "00bdffe0f3c77e27d2cf6f5c70232a2d3e4d9c15"
+ version = "v1.7.3"
+
+[[projects]]
+ digest = "1:e62657cca9badaa308d86e7716083e4c5933bb78e30a17743fc67f50be26f6f4"
name = "github.com/gorilla/websocket"
packages = ["."]
+ pruneopts = "UT"
revision = "c3e18be99d19e6b3e8f1559eea2c161a665c4b6b"
version = "v1.4.1"
[[projects]]
+ digest = "1:f14364057165381ea296e49f8870a9ffce2b8a95e34d6ae06c759106aaef428c"
name = "github.com/hashicorp/go-uuid"
packages = ["."]
+ pruneopts = "UT"
revision = "4f571afc59f3043a65f8fe6bf46d887b10a01d43"
version = "v1.0.1"
[[projects]]
+ digest = "1:c77361e611524ec8f2ad37c408c3c916111a70b6acf806a1200855696bf8fa4d"
name = "github.com/hashicorp/golang-lru"
packages = [
".",
- "simplelru"
+ "simplelru",
]
+ pruneopts = "UT"
revision = "7f827b33c0f158ec5dfbba01bb0b14a4541fd81d"
version = "v0.5.3"
[[projects]]
+ digest = "1:78d28d5b84a26159c67ea51996a230da4bc07cac648adaae1dfb5fc0ec8e40d3"
name = "github.com/imdario/mergo"
packages = ["."]
+ pruneopts = "UT"
revision = "1afb36080aec31e0d1528973ebe6721b191b0369"
version = "v0.3.8"
[[projects]]
branch = "master"
+ digest = "1:62fe3a7ea2050ecbd753a71889026f83d73329337ada66325cbafd5dea5f713d"
name = "github.com/jbenet/go-context"
packages = ["io"]
+ pruneopts = "UT"
revision = "d14ea06fba99483203c19d92cfcd13ebe73135f4"
[[projects]]
+ digest = "1:ae221758bdddd57f5c76f4ee5e4110af32ee62583c46299094697f8f127e63da"
name = "github.com/jcmturner/gofork"
packages = [
"encoding/asn1",
- "x/crypto/pbkdf2"
+ "x/crypto/pbkdf2",
]
+ pruneopts = "UT"
revision = "dc7c13fece037a4a36e2b3c69db4991498d30692"
version = "v1.0.0"
[[projects]]
+ digest = "1:bb81097a5b62634f3e9fec1014657855610c82d19b9a40c17612e32651e35dca"
name = "github.com/jmespath/go-jmespath"
packages = ["."]
+ pruneopts = "UT"
revision = "c2b33e84"
[[projects]]
branch = "master"
+ digest = "1:3daa28dd53624e04229a3499b6bb547b4c467d488e8293b1fc9d67a922713896"
name = "github.com/joncalhoun/qson"
packages = ["."]
+ pruneopts = "UT"
revision = "8a9cab3a62b1b693e7dfa590a215dc6217552803"
[[projects]]
+ digest = "1:beb5b4f42a25056f0aa291b5eadd21e2f2903a05d15dfe7caf7eaee7e12fa972"
name = "github.com/json-iterator/go"
packages = ["."]
+ pruneopts = "UT"
revision = "03217c3e97663914aec3faafde50d081f197a0a2"
version = "v1.1.8"
[[projects]]
+ digest = "1:076c531484852c227471112d49465873aaad47e5ad6e1aec3a5b092a436117ef"
name = "github.com/jstemmer/go-junit-report"
packages = [
".",
"formatter",
- "parser"
+ "parser",
]
+ pruneopts = "UT"
revision = "cc1f095d5cc5eca2844f5c5ea7bb37f6b9bf6cac"
version = "v0.9.1"
[[projects]]
+ digest = "1:4b63210654b1f2b664f74ec434a1bb1cb442b3d75742cc064a10808d1cca6361"
name = "github.com/jtolds/gls"
packages = ["."]
+ pruneopts = "UT"
revision = "b4936e06046bbecbb94cae9c18127ebe510a2cb9"
version = "v4.20"
[[projects]]
+ digest = "1:fd7f169f32c221b096c74e756bda16fe22d3bb448bbf74042fd0700407a1f92f"
name = "github.com/kevinburke/ssh_config"
packages = ["."]
+ pruneopts = "UT"
revision = "6cfae18c12b8934b1afba3ce8159476fdef666ba"
version = "1.0"
[[projects]]
+ digest = "1:69ababe7369aa29063b83d163bdc1b939c1480a6c0d2b44e042d016f35c6e4ad"
name = "github.com/klauspost/compress"
packages = [
"fse",
"huff0",
"snappy",
"zstd",
- "zstd/internal/xxhash"
+ "zstd/internal/xxhash",
]
+ pruneopts = "UT"
revision = "16a4d3d7137cdefd94d420f22b5c20260674b95c"
version = "v1.9.1"
[[projects]]
+ digest = "1:31e761d97c76151dde79e9d28964a812c46efc5baee4085b86f68f0c654450de"
name = "github.com/konsorten/go-windows-terminal-sequences"
packages = ["."]
+ pruneopts = "UT"
revision = "f55edac94c9bbba5d6182a4be46d86a2c9b5b50e"
version = "v1.0.2"
[[projects]]
+ digest = "1:927762c6729b4e72957ba3310e485ed09cf8451c5a637a52fd016a9fe09e7936"
name = "github.com/mailru/easyjson"
packages = [
"buffer",
"jlexer",
- "jwriter"
+ "jwriter",
]
+ pruneopts = "UT"
revision = "1b2b06f5f209fea48ff5922d8bfb2b9ed5d8f00b"
version = "v0.7.0"
[[projects]]
+ digest = "1:88e7456f46448df99fd934c3b90621afbaa14d29172923e36c47f42de386be56"
name = "github.com/minio/minio-go"
packages = [
".",
@@ -472,219 +587,279 @@
"pkg/encrypt",
"pkg/s3signer",
"pkg/s3utils",
- "pkg/set"
+ "pkg/set",
]
+ pruneopts = "UT"
revision = "c6c2912aa5522e5f5a505e6cba30e95f0d8456fa"
version = "v6.0.25"
[[projects]]
+ digest = "1:5d231480e1c64a726869bc4142d270184c419749d34f167646baa21008eb0a79"
name = "github.com/mitchellh/go-homedir"
packages = ["."]
+ pruneopts = "UT"
revision = "af06845cf3004701891bf4fdb884bfe4920b3727"
version = "v1.1.0"
[[projects]]
+ digest = "1:53bc4cd4914cd7cd52139990d5170d6dc99067ae31c56530621b18b35fc30318"
name = "github.com/mitchellh/mapstructure"
packages = ["."]
+ pruneopts = "UT"
revision = "3536a929edddb9a5b34bd6861dc4a9647cb459fe"
version = "v1.1.2"
[[projects]]
+ digest = "1:33422d238f147d247752996a26574ac48dcf472976eda7f5134015f06bf16563"
name = "github.com/modern-go/concurrent"
packages = ["."]
+ pruneopts = "UT"
revision = "bacd9c7ef1dd9b15be4a9909b8ac7a4e313eec94"
version = "1.0.3"
[[projects]]
+ digest = "1:e32bdbdb7c377a07a9a46378290059822efdce5c8d96fe71940d87cb4f918855"
name = "github.com/modern-go/reflect2"
packages = ["."]
+ pruneopts = "UT"
revision = "4b7aa43c6742a2c18fdef89dd197aaae7dac7ccd"
version = "1.0.1"
[[projects]]
+ digest = "1:2ca73053216eb11c8eea2855c8099ad82773638522f91cc0542ec9759163ff3c"
name = "github.com/nats-io/go-nats"
packages = [
".",
"encoders/builtin",
- "util"
+ "util",
]
+ pruneopts = "UT"
revision = "70fe06cee50d4b6f98248d9675fb55f2a3aa7228"
version = "v1.7.2"
[[projects]]
branch = "master"
+ digest = "1:7594f474ecd4b8b2e58f572fffb6db29cbfe9b000260fefff5371865a5a43a83"
name = "github.com/nats-io/go-nats-streaming"
packages = [
".",
- "pb"
+ "pb",
]
+ pruneopts = "UT"
revision = "c4c6d40b2ba9166e155cda77274d615aed57f314"
[[projects]]
+ digest = "1:7cd07cefacf6d1e85399a47a68b32f4b8bef8ca12705bc46efb7d45c0dccb4af"
name = "github.com/nats-io/nkeys"
packages = ["."]
+ pruneopts = "UT"
revision = "abe9e4e0a640435d624e757a9110b0e59f0b6b2c"
version = "v0.1.2"
[[projects]]
+ digest = "1:599f3202ce0a754144ddc4be4c6df9c6ab27b1d722a63ede6b2e0c3a2cc338a8"
name = "github.com/nats-io/nuid"
packages = ["."]
+ pruneopts = "UT"
revision = "4b96681fa6d28dd0ab5fe79bac63b3a493d9ee94"
version = "v1.0.1"
[[projects]]
branch = "master"
+ digest = "1:9135761761efd675ec1bdc76b3449b1ce6865d1852a2fbf25bde19e255acac31"
name = "github.com/nlopes/slack"
packages = [
".",
"internal/errorsx",
"internal/timex",
"slackevents",
- "slackutilsx"
+ "slackutilsx",
]
- revision = "d06c2a2b3249b44a9c5dee8485f5a87497beb9ea"
+ pruneopts = "UT"
+ revision = "d20eeb27bf8f755e0c9aafa933eced3b574b2219"
[[projects]]
+ digest = "1:cef870622e603ac1305922eb5d380455cad27e354355ae7a855d8633ffa66197"
name = "github.com/pierrec/lz4"
packages = [
".",
- "internal/xxh32"
+ "internal/xxh32",
]
+ pruneopts = "UT"
revision = "645f9b948eee34cbcc335c70999f79c29c420fbf"
version = "v2.3.0"
[[projects]]
+ digest = "1:cf31692c14422fa27c83a05292eb5cbe0fb2775972e8f1f8446a71549bd8980b"
name = "github.com/pkg/errors"
packages = ["."]
+ pruneopts = "UT"
revision = "ba968bfe8b2f7e042a574c888954fccecfa385b4"
version = "v0.8.1"
[[projects]]
+ digest = "1:0028cb19b2e4c3112225cd871870f2d9cf49b9b4276531f03438a88e94be86fe"
name = "github.com/pmezard/go-difflib"
packages = ["difflib"]
+ pruneopts = "UT"
revision = "792786c7400a136282c1664665ae0a8db921c6c2"
version = "v1.0.0"
[[projects]]
branch = "master"
+ digest = "1:5bbebe8ac19ecb6c87790a89faa20566e38ed0d6494a1d14c4f5b05d9ce2436c"
name = "github.com/rcrowley/go-metrics"
packages = ["."]
+ pruneopts = "UT"
revision = "cac0b30c2563378d434b5af411844adff8e32960"
[[projects]]
+ digest = "1:ed615c5430ecabbb0fb7629a182da65ecee6523900ac1ac932520860878ffcad"
name = "github.com/robfig/cron"
packages = ["."]
+ pruneopts = "UT"
revision = "b41be1df696709bb6395fe435af20370037c0b4c"
version = "v1.2.0"
[[projects]]
+ digest = "1:d917313f309bda80d27274d53985bc65651f81a5b66b820749ac7f8ef061fd04"
name = "github.com/sergi/go-diff"
packages = ["diffmatchpatch"]
+ pruneopts = "UT"
revision = "1744e2970ca51c86172c8190fadad617561ed6e7"
version = "v1.0.0"
[[projects]]
branch = "master"
+ digest = "1:e21aee53138ca0b77d49f81f15c349b36863beb2f58d7f788136b1c75364e509"
name = "github.com/sirupsen/logrus"
packages = ["."]
+ pruneopts = "UT"
revision = "67a7fdcf741f4d5cee82cb9800994ccfd4393ad0"
[[projects]]
+ digest = "1:237af0cf68bac89e21af72e6cd6b64f388854895e75f82ad08c6c011e1a8286c"
name = "github.com/smartystreets/assertions"
packages = [
".",
"internal/go-diff/diffmatchpatch",
"internal/go-render/render",
- "internal/oglematchers"
+ "internal/oglematchers",
]
+ pruneopts = "UT"
revision = "f487f9de1cd36ebab28235b9373028812fb47cbd"
version = "1.10.1"
[[projects]]
+ digest = "1:483aa658b8b58e357a07ebdfcbcb0202b46f0c0f91e9b63c8807f7d4f5cd30f9"
name = "github.com/smartystreets/goconvey"
packages = [
"convey",
"convey/gotest",
- "convey/reporting"
+ "convey/reporting",
]
+ pruneopts = "UT"
revision = "505e419363375c0dc132d3ac02632a4ee32199ca"
version = "v1.6.4"
[[projects]]
+ digest = "1:524b71991fc7d9246cc7dc2d9e0886ccb97648091c63e30eef619e6862c955dd"
name = "github.com/spf13/pflag"
packages = ["."]
+ pruneopts = "UT"
revision = "2e9d26c8c37aae03e3f9d4e90b7116f5accb7cab"
version = "v1.0.5"
[[projects]]
+ digest = "1:e4ed0afd67bf7be353921665cdac50834c867ff1bba153efc0745b755a7f5905"
name = "github.com/src-d/gcfg"
packages = [
".",
"scanner",
"token",
- "types"
+ "types",
]
+ pruneopts = "UT"
revision = "1ac3a1ac202429a54835fe8408a92880156b489d"
version = "v1.4.0"
[[projects]]
branch = "master"
+ digest = "1:3df46e572883257c46c470dc1796f9bc609d0d0d7339dd10358030649b9beb93"
name = "github.com/streadway/amqp"
packages = ["."]
+ pruneopts = "UT"
revision = "edfb9018d2714e4ec54dbaba37dbfef2bdadf0e4"
[[projects]]
+ digest = "1:ac83cf90d08b63ad5f7e020ef480d319ae890c208f8524622a2f3136e2686b02"
name = "github.com/stretchr/objx"
packages = ["."]
+ pruneopts = "UT"
revision = "477a77ecc69700c7cdeb1fa9e129548e1c1c393c"
version = "v0.1.1"
[[projects]]
+ digest = "1:ad527ce5c6b2426790449db7663fe53f8bb647f9387295406794c8be001238da"
name = "github.com/stretchr/testify"
packages = [
"assert",
- "mock"
+ "mock",
]
+ pruneopts = "UT"
revision = "221dbe5ed46703ee255b1da0dec05086f5035f62"
version = "v1.4.0"
[[projects]]
+ digest = "1:5a68167017eaa32aa408397806b9d69815244238ed774439a8863ef4bc329eeb"
name = "github.com/tidwall/gjson"
packages = ["."]
+ pruneopts = "UT"
revision = "c34bf81952c067718854115564f8e55978be5e1d"
version = "v1.3.4"
[[projects]]
+ digest = "1:8453ddbed197809ee8ca28b06bd04e127bec9912deb4ba451fea7a1eca578328"
name = "github.com/tidwall/match"
packages = ["."]
+ pruneopts = "UT"
revision = "33827db735fff6510490d69a8622612558a557ed"
version = "v1.0.1"
[[projects]]
+ digest = "1:ddfe0a54e5f9b29536a6d7b2defa376f2cb2b6e4234d676d7ff214d5b097cb50"
name = "github.com/tidwall/pretty"
packages = ["."]
+ pruneopts = "UT"
revision = "1166b9ac2b65e46a43d8618d30d1554f4652d49b"
version = "v1.0.0"
[[projects]]
+ digest = "1:b70c951ba6fdeecfbd50dabe95aa5e1b973866ae9abbece46ad60348112214f2"
name = "github.com/tidwall/sjson"
packages = ["."]
+ pruneopts = "UT"
revision = "25fb082a20e29e83fb7b7ef5f5919166aad1f084"
version = "v1.0.4"
[[projects]]
+ digest = "1:766db8705204fd893db77ff5fde228362fbceac616b87ccb9976518095aac8ce"
name = "github.com/xanzy/go-gitlab"
packages = ["."]
- revision = "457d4d018eaa1fad8e6c63502cebcd11ba60164e"
- version = "v0.22.0"
+ pruneopts = "UT"
+ revision = "87a6b9db49fa4bd6efeaeec450b0c5661f94fcb5"
+ version = "v0.21.0"
[[projects]]
+ digest = "1:172f94a6b3644a8f9e6b5e5b7fc9fe1e42d424f52a0300b2e7ab1e57db73f85d"
name = "github.com/xanzy/ssh-agent"
packages = ["."]
+ pruneopts = "UT"
revision = "6a3e2ff9e7c564f36873c2e36413f634534f1c44"
version = "v0.2.1"
[[projects]]
+ digest = "1:07ca513e3b295b9aeb1f0f6fbefb8102139a2764ce0f28d02040c0eb2dc276dd"
name = "go.opencensus.io"
packages = [
".",
@@ -703,13 +878,15 @@
"trace",
"trace/internal",
"trace/propagation",
- "trace/tracestate"
+ "trace/tracestate",
]
+ pruneopts = "UT"
revision = "59d1ce35d30f3c25ba762169da2a37eab6ffa041"
version = "v0.22.1"
[[projects]]
branch = "master"
+ digest = "1:35041d2389057ddabc32b374768b133c795fa7f6eb9bcd80ac9bd820d86ea8a4"
name = "golang.org/x/crypto"
packages = [
"argon2",
@@ -732,30 +909,36 @@
"ssh",
"ssh/agent",
"ssh/knownhosts",
- "ssh/terminal"
+ "ssh/terminal",
]
- revision = "f4817d981bb690635456c5c1c6aa0585e5d45891"
+ pruneopts = "UT"
+ revision = "c7e5f84aec591254278750bee18f39e5dd19cdb5"
[[projects]]
branch = "master"
+ digest = "1:b1444bc98b5838c3116ed23e231fee4fa8509f975abd96e5d9e67e572dd01604"
name = "golang.org/x/exp"
packages = [
"apidiff",
- "cmd/apidiff"
+ "cmd/apidiff",
]
+ pruneopts = "UT"
revision = "a1ab85dbe136a36c66fbea07de5e3d62a0ce60ad"
[[projects]]
branch = "master"
+ digest = "1:21d7bad9b7da270fd2d50aba8971a041bd691165c95096a2a4c68db823cbc86a"
name = "golang.org/x/lint"
packages = [
".",
- "golint"
+ "golint",
]
+ pruneopts = "UT"
revision = "16217165b5de779cb6a5e4fc81fa9c1166fda457"
[[projects]]
branch = "master"
+ digest = "1:c01950158c9535178651a56e3a82b2088560555e9f69b1c9a0f79afbc8619c43"
name = "golang.org/x/net"
packages = [
"context",
@@ -769,42 +952,50 @@
"proxy",
"publicsuffix",
"trace",
- "websocket"
+ "websocket",
]
- revision = "7e6e90b9ea8824b29cbeee76d03ef838c9187418"
+ pruneopts = "UT"
+ revision = "a882066a44e04a21d46e51451c71e131344e830e"
[[projects]]
branch = "master"
+ digest = "1:31e33f76456ccf54819ab4a646cf01271d1a99d7712ab84bf1a9e7b61cd2031b"
name = "golang.org/x/oauth2"
packages = [
".",
"google",
"internal",
"jws",
- "jwt"
+ "jwt",
]
+ pruneopts = "UT"
revision = "0f29369cfe4552d0e4bcddc57cc75f4d7e672a33"
[[projects]]
branch = "master"
+ digest = "1:a2fc247e64b5dafd3251f12d396ec85f163d5bb38763c4997856addddf6e78d8"
name = "golang.org/x/sync"
packages = [
"errgroup",
- "semaphore"
+ "semaphore",
]
+ pruneopts = "UT"
revision = "cd5d95a43a6e21273425c7ae415d3df9ea832eeb"
[[projects]]
branch = "master"
+ digest = "1:92c957c2713e817f2c5e25735152837312e3e0458e784a5e168d725474ac8995"
name = "golang.org/x/sys"
packages = [
"cpu",
"unix",
- "windows"
+ "windows",
]
+ pruneopts = "UT"
revision = "c1f44814a5cd81a6d1cb589ef1e528bc5d305e07"
[[projects]]
+ digest = "1:66a2f252a58b4fbbad0e4e180e1d85a83c222b6bce09c3dcdef3dc87c72eda7c"
name = "golang.org/x/text"
packages = [
"collate",
@@ -823,19 +1014,23 @@
"unicode/cldr",
"unicode/norm",
"unicode/rangetable",
- "width"
+ "width",
]
+ pruneopts = "UT"
revision = "342b2e1fbaa52c93f31447ad2c6abc048c63e475"
version = "v0.3.2"
[[projects]]
branch = "master"
+ digest = "1:a2f668c709f9078828e99cb1768cb02e876cb81030545046a32b54b2ac2a9ea8"
name = "golang.org/x/time"
packages = ["rate"]
+ pruneopts = "UT"
revision = "555d28b269f0569763d25dbe1a237ae74c6bcc82"
[[projects]]
branch = "master"
+ digest = "1:403cd4290937cc1ce67cd607806ee217720305519109b6c49d88a44144c1e100"
name = "golang.org/x/tools"
packages = [
"cmd/goimports",
@@ -856,11 +1051,13 @@
"internal/imports",
"internal/module",
"internal/semver",
- "internal/span"
+ "internal/span",
]
- revision = "f7ea15e60b12ba031eaab7e3247e845e5c7eba73"
+ pruneopts = "UT"
+ revision = "689d0f08e67ae0c77c260e137ac6a3729498c92f"
[[projects]]
+ digest = "1:526726f1b56fe59206fd9ade203359f20d0958c2feaa6c263f677cf655944c92"
name = "google.golang.org/api"
packages = [
"googleapi/transport",
@@ -871,12 +1068,14 @@
"transport",
"transport/grpc",
"transport/http",
- "transport/http/internal/propagation"
+ "transport/http/internal/propagation",
]
+ pruneopts = "UT"
revision = "4f42dad4690a01d7f6fa461106c63889ff1be027"
version = "v0.13.0"
[[projects]]
+ digest = "1:c98e9b93e6d178378530b920fe6e1aa4b3dd4972872111e83827746aa1f33ded"
name = "google.golang.org/appengine"
packages = [
".",
@@ -890,13 +1089,15 @@
"internal/socket",
"internal/urlfetch",
"socket",
- "urlfetch"
+ "urlfetch",
]
+ pruneopts = "UT"
revision = "971852bfffca25b069c31162ae8f247a3dba083b"
version = "v1.6.5"
[[projects]]
branch = "master"
+ digest = "1:fb32dd440d7b296fd516c15af931e13cab6b0ce746f1b077c5d79825bbde0037"
name = "google.golang.org/genproto"
packages = [
"googleapis/api/annotations",
@@ -904,11 +1105,13 @@
"googleapis/pubsub/v1",
"googleapis/rpc/status",
"googleapis/type/expr",
- "protobuf/field_mask"
+ "protobuf/field_mask",
]
+ pruneopts = "UT"
revision = "919d9bdd9fe6f1a5dd95ce5d5e4cdb8fd3c516d0"
[[projects]]
+ digest = "1:56380851a4d733663a711bd5ad5917ea5cb825661364aa13397c6af2061be8bc"
name = "google.golang.org/grpc"
packages = [
".",
@@ -956,36 +1159,46 @@
"serviceconfig",
"stats",
"status",
- "tap"
+ "tap",
]
+ pruneopts = "UT"
revision = "9d331e2b02dd47daeecae02790f61cc88dc75a64"
version = "v1.25.0"
[[projects]]
+ digest = "1:2d1fbdc6777e5408cabeb02bf336305e724b925ff4546ded0fa8715a7267922a"
name = "gopkg.in/inf.v0"
packages = ["."]
+ pruneopts = "UT"
revision = "d2d2541c53f18d2a059457998ce2876cc8e67cbf"
version = "v0.9.1"
[[projects]]
+ digest = "1:2a2e303bb32696f9250d4ba3d8c25c7849a6bff66a13a9f9caf4f34774b5ee51"
name = "gopkg.in/ini.v1"
packages = ["."]
- revision = "8ee0b789944d2b0a7297c671009b3e7bc977376f"
- version = "v1.50.0"
+ pruneopts = "UT"
+ revision = "1eb383f13cde0e7be091181a93b58574638129f0"
+ version = "v1.49.0"
[[projects]]
+ digest = "1:c902038ee2d6f964d3b9f2c718126571410c5d81251cbab9fe58abd37803513c"
name = "gopkg.in/jcmturner/aescts.v1"
packages = ["."]
+ pruneopts = "UT"
revision = "f6abebb3171c4c1b1fea279cb7c7325020a26290"
version = "v1.0.1"
[[projects]]
+ digest = "1:a1a3e185c03d79a7452d5d5b4c91be4cc433f55e6ed3a35233d852c966e39013"
name = "gopkg.in/jcmturner/dnsutils.v1"
packages = ["."]
+ pruneopts = "UT"
revision = "13eeb8d49ffb74d7a75784c35e4d900607a3943c"
version = "v1.0.1"
[[projects]]
+ digest = "1:653c1ef9be253f28c38612cc0fb0571dd440a3d61a97f82e6205d53942a7b4a9"
name = "gopkg.in/jcmturner/gokrb5.v5"
packages = [
"asn1tools",
@@ -1018,12 +1231,14 @@
"messages",
"mstypes",
"pac",
- "types"
+ "types",
]
+ pruneopts = "UT"
revision = "32ba44ca5b42f17a4a9f33ff4305e70665a1bc0f"
version = "v5.3.0"
[[projects]]
+ digest = "1:dc01a587d07be012625ba63df6d4224ae6d7a83e79bfebde6d987c10538d66dd"
name = "gopkg.in/jcmturner/gokrb5.v7"
packages = [
"asn1tools",
@@ -1055,39 +1270,47 @@
"krberror",
"messages",
"pac",
- "types"
+ "types",
]
+ pruneopts = "UT"
revision = "363118e62befa8a14ff01031c025026077fe5d6d"
version = "v7.3.0"
[[projects]]
+ digest = "1:917e312d1c83bac01db5771433a141f7e4754df0ebe83d2e8edc821320aff849"
name = "gopkg.in/jcmturner/rpc.v0"
packages = ["ndr"]
+ pruneopts = "UT"
revision = "4480c480c9cd343b54b0acb5b62261cbd33d7adf"
version = "v0.0.2"
[[projects]]
+ digest = "1:0f16d9c577198e3b8d3209f5a89aabe679525b2aba2a7548714e973035c0e232"
name = "gopkg.in/jcmturner/rpc.v1"
packages = [
"mstypes",
- "ndr"
+ "ndr",
]
+ pruneopts = "UT"
revision = "99a8ce2fbf8b8087b6ed12a37c61b10f04070043"
version = "v1.1.0"
[[projects]]
+ digest = "1:eb27cfcaf8d7e4155224dd0a209f1d0ab19784fef01be142638b78b7b6becd6b"
name = "gopkg.in/src-d/go-billy.v4"
packages = [
".",
"helper/chroot",
"helper/polyfill",
"osfs",
- "util"
+ "util",
]
+ pruneopts = "UT"
revision = "780403cfc1bc95ff4d07e7b26db40a6186c5326e"
version = "v4.3.2"
[[projects]]
+ digest = "1:b2ad0a18676cd4d5b4b180709c1ea34dbabd74b3d7db0cc01e6d287d5f1e3a99"
name = "gopkg.in/src-d/go-git.v4"
packages = [
".",
@@ -1130,24 +1353,30 @@
"utils/merkletrie/filesystem",
"utils/merkletrie/index",
"utils/merkletrie/internal/frame",
- "utils/merkletrie/noder"
+ "utils/merkletrie/noder",
]
+ pruneopts = "UT"
revision = "0d1a009cbb604db18be960db5f1525b99a55d727"
version = "v4.13.1"
[[projects]]
+ digest = "1:78d374b493e747afa9fbb2119687e3740a7fb8d0ebabddfef0a012593aaecbb3"
name = "gopkg.in/warnings.v0"
packages = ["."]
+ pruneopts = "UT"
revision = "ec4a0fea49c7b46c2aeb0b51aac55779c607e52b"
version = "v0.1.2"
[[projects]]
+ digest = "1:f26a5d382387e03a40d1471dddfba85dfff9bf05352d7e42d37612677c4d3c5c"
name = "gopkg.in/yaml.v2"
packages = ["."]
+ pruneopts = "UT"
revision = "f90ceb4f409096b60e2e9076b38b304b8246e5fa"
version = "v2.2.5"
[[projects]]
+ digest = "1:131158a88aad1f94854d0aa21a64af2802d0a470fb0f01cb33c04fafd2047111"
name = "honnef.co/go/tools"
packages = [
"arg",
@@ -1174,13 +1403,15 @@
"staticcheck/vrp",
"stylecheck",
"unused",
- "version"
+ "version",
]
+ pruneopts = "UT"
revision = "afd67930eec2a9ed3e9b19f684d17a062285f16a"
version = "2019.2.3"
[[projects]]
branch = "release-1.15"
+ digest = "1:d89afbf3588e87d2c9e6efdd5528d249b32d23a12fbd7ec324f3cb373c6fb76c"
name = "k8s.io/api"
packages = [
"admissionregistration/v1beta1",
@@ -1218,12 +1449,14 @@
"settings/v1alpha1",
"storage/v1",
"storage/v1alpha1",
- "storage/v1beta1"
+ "storage/v1beta1",
]
+ pruneopts = "UT"
revision = "3a12735a829ac2f7817379647da6d47c39327512"
[[projects]]
branch = "release-1.15"
+ digest = "1:b2ba899970541943ec0a2f1bdef707b029d8591236fb7dab27b222f84037d8ef"
name = "k8s.io/apimachinery"
packages = [
"pkg/api/errors",
@@ -1270,12 +1503,13 @@
"pkg/watch",
"third_party/forked/golang/json",
"third_party/forked/golang/netutil",
- "third_party/forked/golang/reflect"
+ "third_party/forked/golang/reflect",
]
+ pruneopts = "UT"
revision = "31ade1b30762be61c32b2e8db2a11aa8b0b8960e"
[[projects]]
- branch = "release-12.0"
+ digest = "1:25e5278fc841f46e9b465a8c67f3304b32ac85ced3b7057438318db79f71c1ed"
name = "k8s.io/client-go"
packages = [
"discovery",
@@ -1468,12 +1702,15 @@
"util/homedir",
"util/keyutil",
"util/retry",
- "util/workqueue"
+ "util/workqueue",
]
- revision = "5f2132fc4383659da452dba21a1c6c9890b0e062"
+ pruneopts = "UT"
+ revision = "78d2af792babf2dd937ba2e2a8d99c753a5eda89"
+ version = "v12.0.0"
[[projects]]
branch = "release-1.15"
+ digest = "1:f41480fd8c5f54b13326ee0f2ee398d5734789b790dbc4db57f9b08a0d63139a"
name = "k8s.io/code-generator"
packages = [
"cmd/client-gen",
@@ -1485,11 +1722,13 @@
"cmd/client-gen/path",
"cmd/client-gen/types",
"pkg/namer",
- "pkg/util"
+ "pkg/util",
]
+ pruneopts = "T"
revision = "18da4a14b22b17d2fa761e50037fabfbacec225b"
[[projects]]
+ digest = "1:a9f99f1c11620be972d49d2e4e296031a5fbc168ada49a9e618d9b35f751f119"
name = "k8s.io/gengo"
packages = [
"args",
@@ -1499,18 +1738,22 @@
"generator",
"namer",
"parser",
- "types"
+ "types",
]
+ pruneopts = "T"
revision = "b90029ef6cd877cb3f422d75b3a07707e3aac6b7"
[[projects]]
+ digest = "1:93e82f25d75aba18436ad1ac042cb49493f096011f2541075721ed6f9e05c044"
name = "k8s.io/klog"
packages = ["."]
+ pruneopts = "UT"
revision = "2ca9ad30301bf30a8a6e0fa2110db6b8df699a91"
version = "v1.0.0"
[[projects]]
branch = "master"
+ digest = "1:94ad85c6f3cfad58b9f602b9a2d4af530e8db8f861bca708c4dd236f771ea1a4"
name = "k8s.io/kube-openapi"
packages = [
"cmd/openapi-gen",
@@ -1519,29 +1762,128 @@
"pkg/generators",
"pkg/generators/rules",
"pkg/util/proto",
- "pkg/util/sets"
+ "pkg/util/sets",
]
- revision = "30be4d16710ac61bce31eb28a01054596fe6a9f1"
+ pruneopts = "UT"
+ revision = "0270cf2f1c1d995d34b36019a6f65d58e6e33ad4"
[[projects]]
branch = "master"
+ digest = "1:2d3f59daa4b479ff4e100a2e1d8fea6780040fdadc177869531fe4cc29407f55"
name = "k8s.io/utils"
packages = [
"buffer",
"integer",
- "trace"
+ "trace",
]
+ pruneopts = "UT"
revision = "2b95a09bc58df43d4032504619706b6a38293a47"
[[projects]]
+ digest = "1:7719608fe0b52a4ece56c2dde37bedd95b938677d1ab0f84b8a7852e4c59f849"
name = "sigs.k8s.io/yaml"
packages = ["."]
+ pruneopts = "UT"
revision = "fd68e9863619f6ec2fdd8625fe1f02e7c877e480"
version = "v1.1.0"
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
- inputs-digest = "df67feb7bfc29e71c524d42fa1f6878e4b4ffbe085ece6cf09c30a8875269e0f"
+ input-imports = [
+ "cloud.google.com/go/pubsub",
+ "github.com/Knetic/govaluate",
+ "github.com/Shopify/sarama",
+ "github.com/argoproj/argo/pkg/apis/workflow/v1alpha1",
+ "github.com/aws/aws-sdk-go/aws",
+ "github.com/aws/aws-sdk-go/aws/credentials",
+ "github.com/aws/aws-sdk-go/aws/session",
+ "github.com/aws/aws-sdk-go/service/sns",
+ "github.com/aws/aws-sdk-go/service/sqs",
+ "github.com/colinmarc/hdfs",
+ "github.com/eclipse/paho.mqtt.golang",
+ "github.com/fsnotify/fsnotify",
+ "github.com/ghodss/yaml",
+ "github.com/go-openapi/spec",
+ "github.com/gobwas/glob",
+ "github.com/gogo/protobuf/protoc-gen-gofast",
+ "github.com/gogo/protobuf/protoc-gen-gogofast",
+ "github.com/golang/protobuf/proto",
+ "github.com/golang/protobuf/protoc-gen-go",
+ "github.com/google/go-github/github",
+ "github.com/google/uuid",
+ "github.com/gorilla/mux",
+ "github.com/joncalhoun/qson",
+ "github.com/minio/minio-go",
+ "github.com/mitchellh/mapstructure",
+ "github.com/nats-io/go-nats",
+ "github.com/nats-io/go-nats-streaming",
+ "github.com/nlopes/slack",
+ "github.com/nlopes/slack/slackevents",
+ "github.com/pkg/errors",
+ "github.com/robfig/cron",
+ "github.com/sirupsen/logrus",
+ "github.com/smartystreets/goconvey/convey",
+ "github.com/streadway/amqp",
+ "github.com/stretchr/testify/assert",
+ "github.com/stretchr/testify/mock",
+ "github.com/tidwall/gjson",
+ "github.com/tidwall/sjson",
+ "github.com/xanzy/go-gitlab",
+ "golang.org/x/crypto/ssh",
+ "google.golang.org/api/option",
+ "google.golang.org/grpc",
+ "google.golang.org/grpc/codes",
+ "google.golang.org/grpc/connectivity",
+ "google.golang.org/grpc/metadata",
+ "google.golang.org/grpc/status",
+ "gopkg.in/jcmturner/gokrb5.v5/client",
+ "gopkg.in/jcmturner/gokrb5.v5/config",
+ "gopkg.in/jcmturner/gokrb5.v5/credentials",
+ "gopkg.in/jcmturner/gokrb5.v5/keytab",
+ "gopkg.in/src-d/go-git.v4",
+ "gopkg.in/src-d/go-git.v4/config",
+ "gopkg.in/src-d/go-git.v4/plumbing",
+ "gopkg.in/src-d/go-git.v4/plumbing/transport",
+ "gopkg.in/src-d/go-git.v4/plumbing/transport/http",
+ "gopkg.in/src-d/go-git.v4/plumbing/transport/ssh",
+ "k8s.io/api/apps/v1",
+ "k8s.io/api/core/v1",
+ "k8s.io/apimachinery/pkg/api/errors",
+ "k8s.io/apimachinery/pkg/apis/meta/v1",
+ "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured",
+ "k8s.io/apimachinery/pkg/fields",
+ "k8s.io/apimachinery/pkg/labels",
+ "k8s.io/apimachinery/pkg/runtime",
+ "k8s.io/apimachinery/pkg/runtime/schema",
+ "k8s.io/apimachinery/pkg/runtime/serializer",
+ "k8s.io/apimachinery/pkg/selection",
+ "k8s.io/apimachinery/pkg/types",
+ "k8s.io/apimachinery/pkg/util/intstr",
+ "k8s.io/apimachinery/pkg/util/runtime",
+ "k8s.io/apimachinery/pkg/util/wait",
+ "k8s.io/apimachinery/pkg/watch",
+ "k8s.io/client-go/discovery",
+ "k8s.io/client-go/discovery/fake",
+ "k8s.io/client-go/dynamic",
+ "k8s.io/client-go/dynamic/dynamicinformer",
+ "k8s.io/client-go/dynamic/fake",
+ "k8s.io/client-go/informers/core/v1",
+ "k8s.io/client-go/kubernetes",
+ "k8s.io/client-go/kubernetes/fake",
+ "k8s.io/client-go/kubernetes/scheme",
+ "k8s.io/client-go/rest",
+ "k8s.io/client-go/testing",
+ "k8s.io/client-go/tools/cache",
+ "k8s.io/client-go/tools/clientcmd",
+ "k8s.io/client-go/tools/portforward",
+ "k8s.io/client-go/transport/spdy",
+ "k8s.io/client-go/util/flowcontrol",
+ "k8s.io/client-go/util/workqueue",
+ "k8s.io/code-generator/cmd/client-gen",
+ "k8s.io/gengo/examples/deepcopy-gen",
+ "k8s.io/kube-openapi/cmd/openapi-gen",
+ "k8s.io/kube-openapi/pkg/common",
+ ]
solver-name = "gps-cdcl"
solver-version = 1
diff --git a/Gopkg.toml b/Gopkg.toml
index 3b77506fe9..5a5f52b008 100644
--- a/Gopkg.toml
+++ b/Gopkg.toml
@@ -92,6 +92,10 @@ required = [
name = "github.com/Knetic/govaluate"
branch = "master"
+[[constraint]]
+ name = "github.com/gorilla/mux"
+ version = "v1.7.3"
+
[[constraint]]
name = "github.com/colinmarc/hdfs"
revision = "48eb8d6c34a97ffc73b406356f0f2e1c569b42a5"
@@ -109,7 +113,7 @@ required = [
name = "k8s.io/apimachinery"
[[override]]
- branch = "release-12.0"
+ version = "v12.0.0"
name = "k8s.io/client-go"
[prune]
diff --git a/Makefile b/Makefile
index d272d50b30..02583d9876 100644
--- a/Makefile
+++ b/Makefile
@@ -17,7 +17,7 @@ override LDFLAGS += \
# docker image publishing options
DOCKER_PUSH?=true
IMAGE_NAMESPACE?=argoproj
-IMAGE_TAG?=v0.11
+IMAGE_TAG?=v0.12-rc
ifeq (${DOCKER_PUSH},true)
ifndef IMAGE_NAMESPACE
@@ -35,13 +35,13 @@ endif
# Build the project images
.DELETE_ON_ERROR:
-all: sensor-linux sensor-controller-linux gateway-controller-linux gateway-client-linux webhook-linux calendar-linux resource-linux artifact-linux file-linux nats-linux kafka-linux amqp-linux mqtt-linux storage-grid-linux github-linux hdfs-linux gitlab-linux sns-linux sqs-linux pubsub-linux slack-linux
+all: sensor-linux sensor-controller-linux gateway-controller-linux gateway-client-linux webhook-linux calendar-linux resource-linux minio-linux file-linux nats-linux kafka-linux amqp-linux mqtt-linux storage-grid-linux github-linux hdfs-linux gitlab-linux sns-linux sqs-linux pubsub-linux slack-linux
-all-images: sensor-image sensor-controller-image gateway-controller-image gateway-client-image webhook-image calendar-image resource-image artifact-image file-image nats-image kafka-image amqp-image mqtt-image storage-grid-image github-image gitlab-image sns-image pubsub-image hdfs-image sqs-image slack-image
+all-images: sensor-image sensor-controller-image gateway-controller-image gateway-client-image webhook-image calendar-image resource-image minio-image file-image nats-image kafka-image amqp-image mqtt-image storage-grid-image github-image gitlab-image sns-image pubsub-image hdfs-image sqs-image slack-image
all-controller-images: sensor-controller-image gateway-controller-image
-all-core-gateway-images: webhook-image calendar-image artifact-image file-image nats-image kafka-image amqp-image mqtt-image resource-image
+all-core-gateway-images: webhook-image calendar-image minio-image file-image nats-image kafka-image amqp-image mqtt-image resource-image
.PHONY: all clean test
@@ -58,7 +58,7 @@ sensor-image: sensor-linux
# Sensor controller
sensor-controller:
- go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/sensor-controller ./cmd/controllers/sensor
+ go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/sensor-controller ./controllers/sensor/cmd
sensor-controller-linux:
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 make sensor-controller
@@ -69,7 +69,7 @@ sensor-controller-image: sensor-controller-linux
# Gateway controller
gateway-controller:
- go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/gateway-controller ./cmd/controllers/gateway/main.go
+ go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/gateway-controller ./controllers/gateway/cmd
gateway-controller-linux:
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 make gateway-controller
@@ -81,196 +81,196 @@ gateway-controller-image: gateway-controller-linux
# Gateway client binary
gateway-client:
- go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/gateway-client ./gateways/cmd/main.go
+ go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/gateway-client ./gateways/client
gateway-client-linux:
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 make gateway-client
gateway-client-image: gateway-client-linux
- docker build -t $(IMAGE_PREFIX)gateway-client:$(IMAGE_TAG) -f ./gateways/Dockerfile .
+ docker build -t $(IMAGE_PREFIX)gateway-client:$(IMAGE_TAG) -f ./gateways/client/Dockerfile .
@if [ "$(DOCKER_PUSH)" = "true" ] ; then docker push $(IMAGE_PREFIX)gateway-client:$(IMAGE_TAG) ; fi
# gateway binaries
webhook:
- go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/webhook-gateway ./gateways/core/webhook/cmd/
+ go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/webhook-gateway ./gateways/server/webhook/cmd/
webhook-linux:
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 make webhook
webhook-image: webhook-linux
- docker build -t $(IMAGE_PREFIX)webhook-gateway:$(IMAGE_TAG) -f ./gateways/core/webhook/Dockerfile .
+ docker build -t $(IMAGE_PREFIX)webhook-gateway:$(IMAGE_TAG) -f ./gateways/server/webhook/Dockerfile .
@if [ "$(DOCKER_PUSH)" = "true" ] ; then docker push $(IMAGE_PREFIX)webhook-gateway:$(IMAGE_TAG) ; fi
calendar:
- go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/calendar-gateway ./gateways/core/calendar/cmd/
+ go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/calendar-gateway ./gateways/server/calendar/cmd/
calendar-linux:
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 make calendar
calendar-image: calendar-linux
- docker build -t $(IMAGE_PREFIX)calendar-gateway:$(IMAGE_TAG) -f ./gateways/core/calendar/Dockerfile .
+ docker build -t $(IMAGE_PREFIX)calendar-gateway:$(IMAGE_TAG) -f ./gateways/server/calendar/Dockerfile .
@if [ "$(DOCKER_PUSH)" = "true" ] ; then docker push $(IMAGE_PREFIX)calendar-gateway:$(IMAGE_TAG) ; fi
resource:
- go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/resource-gateway ./gateways/core/resource/cmd
+ go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/resource-gateway ./gateways/server/resource/cmd
resource-linux:
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 make resource
resource-image: resource-linux
- docker build -t $(IMAGE_PREFIX)resource-gateway:$(IMAGE_TAG) -f ./gateways/core/resource/Dockerfile .
+ docker build -t $(IMAGE_PREFIX)resource-gateway:$(IMAGE_TAG) -f ./gateways/server/resource/Dockerfile .
@if [ "$(DOCKER_PUSH)" = "true" ] ; then docker push $(IMAGE_PREFIX)resource-gateway:$(IMAGE_TAG) ; fi
-artifact:
- go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/artifact-gateway ./gateways/core/artifact/cmd
+minio:
+ go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/minio-gateway ./gateways/server/minio/cmd
-artifact-linux:
- CGO_ENABLED=0 GOOS=linux GOARCH=amd64 make artifact
+minio-linux:
+ CGO_ENABLED=0 GOOS=linux GOARCH=amd64 make minio
-artifact-image: artifact-linux
- docker build -t $(IMAGE_PREFIX)artifact-gateway:$(IMAGE_TAG) -f ./gateways/core/artifact/Dockerfile .
- @if [ "$(DOCKER_PUSH)" = "true" ] ; then docker push $(IMAGE_PREFIX)artifact-gateway:$(IMAGE_TAG) ; fi
+minio-image: minio-linux
+ docker build -t $(IMAGE_PREFIX)minio-gateway:$(IMAGE_TAG) -f ./gateways/server/minio/Dockerfile .
+ @if [ "$(DOCKER_PUSH)" = "true" ] ; then docker push $(IMAGE_PREFIX)minio-gateway:$(IMAGE_TAG) ; fi
file:
- go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/file-gateway ./gateways/core/file/cmd
+ go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/file-gateway ./gateways/server/file/cmd
file-linux:
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 make file
file-image: file-linux
- docker build -t $(IMAGE_PREFIX)file-gateway:$(IMAGE_TAG) -f ./gateways/core/file/Dockerfile .
+ docker build -t $(IMAGE_PREFIX)file-gateway:$(IMAGE_TAG) -f ./gateways/server/file/Dockerfile .
@if [ "$(DOCKER_PUSH)" = "true" ] ; then docker push $(IMAGE_PREFIX)file-gateway:$(IMAGE_TAG) ; fi
#Stream gateways
nats:
- go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/nats-gateway ./gateways/core/stream/nats/cmd
+ go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/nats-gateway ./gateways/server/nats/cmd
nats-linux:
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 make nats
nats-image: nats-linux
- docker build -t $(IMAGE_PREFIX)nats-gateway:$(IMAGE_TAG) -f ./gateways/core/stream/nats/Dockerfile .
+ docker build -t $(IMAGE_PREFIX)nats-gateway:$(IMAGE_TAG) -f ./gateways/server/nats/Dockerfile .
@if [ "$(DOCKER_PUSH)" = "true" ] ; then docker push $(IMAGE_PREFIX)nats-gateway:$(IMAGE_TAG) ; fi
kafka:
- go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/kafka-gateway ./gateways/core/stream/kafka/cmd
+ go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/kafka-gateway ./gateways/server/kafka/cmd
kafka-linux:
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 make kafka
kafka-image: kafka-linux
- docker build -t $(IMAGE_PREFIX)kafka-gateway:$(IMAGE_TAG) -f ./gateways/core/stream/kafka/Dockerfile .
+ docker build -t $(IMAGE_PREFIX)kafka-gateway:$(IMAGE_TAG) -f ./gateways/server/kafka/Dockerfile .
@if [ "$(DOCKER_PUSH)" = "true" ] ; then docker push $(IMAGE_PREFIX)kafka-gateway:$(IMAGE_TAG) ; fi
amqp:
- go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/amqp-gateway ./gateways/core/stream/amqp/cmd
+ go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/amqp-gateway ./gateways/server/amqp/cmd
amqp-linux:
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 make amqp
amqp-image: amqp-linux
- docker build -t $(IMAGE_PREFIX)amqp-gateway:$(IMAGE_TAG) -f ./gateways/core/stream/amqp/Dockerfile .
+ docker build -t $(IMAGE_PREFIX)amqp-gateway:$(IMAGE_TAG) -f ./gateways/server/amqp/Dockerfile .
@if [ "$(DOCKER_PUSH)" = "true" ] ; then docker push $(IMAGE_PREFIX)amqp-gateway:$(IMAGE_TAG) ; fi
mqtt:
- go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/mqtt-gateway ./gateways/core/stream/mqtt/cmd
+ go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/mqtt-gateway ./gateways/server/mqtt/cmd
mqtt-linux:
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 make mqtt
mqtt-image: mqtt-linux
- docker build -t $(IMAGE_PREFIX)mqtt-gateway:$(IMAGE_TAG) -f ./gateways/core/stream/mqtt/Dockerfile .
+ docker build -t $(IMAGE_PREFIX)mqtt-gateway:$(IMAGE_TAG) -f ./gateways/server/mqtt/Dockerfile .
@if [ "$(DOCKER_PUSH)" = "true" ] ; then docker push $(IMAGE_PREFIX)mqtt-gateway:$(IMAGE_TAG) ; fi
# Custom gateways
storage-grid:
- go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/storagegrid-gateway ./gateways/community/storagegrid/cmd
+ go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/storagegrid-gateway ./gateways/server/storagegrid/cmd
storage-grid-linux:
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 make storage-grid
storage-grid-image: storage-grid-linux
- docker build -t $(IMAGE_PREFIX)storage-grid-gateway:$(IMAGE_TAG) -f ./gateways/community/storagegrid/Dockerfile .
+ docker build -t $(IMAGE_PREFIX)storage-grid-gateway:$(IMAGE_TAG) -f ./gateways/server/storagegrid/Dockerfile .
@if [ "$(DOCKER_PUSH)" = "true" ] ; then docker push $(IMAGE_PREFIX)storage-grid-gateway:$(IMAGE_TAG) ; fi
gitlab:
- go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/gitlab-gateway ./gateways/community/gitlab/cmd
+ go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/gitlab-gateway ./gateways/server/gitlab/cmd
gitlab-linux:
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 make gitlab
gitlab-image: gitlab-linux
- docker build -t $(IMAGE_PREFIX)gitlab-gateway:$(IMAGE_TAG) -f ./gateways/community/gitlab/Dockerfile .
+ docker build -t $(IMAGE_PREFIX)gitlab-gateway:$(IMAGE_TAG) -f ./gateways/server/gitlab/Dockerfile .
@if [ "$(DOCKER_PUSH)" = "true" ] ; then docker push $(IMAGE_PREFIX)gitlab-gateway:$(IMAGE_TAG) ; fi
github:
- go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/github-gateway ./gateways/community/github/cmd
+ go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/github-gateway ./gateways/server/github/cmd
github-linux:
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 make github
github-image: github-linux
- docker build -t $(IMAGE_PREFIX)github-gateway:$(IMAGE_TAG) -f ./gateways/community/github/Dockerfile .
+ docker build -t $(IMAGE_PREFIX)github-gateway:$(IMAGE_TAG) -f ./gateways/server/github/Dockerfile .
@if [ "$(DOCKER_PUSH)" = "true" ] ; then docker push $(IMAGE_PREFIX)github-gateway:$(IMAGE_TAG) ; fi
sns:
- go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/aws-sns-gateway ./gateways/community/aws-sns/cmd
+ go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/aws-sns-gateway ./gateways/server/aws-sns/cmd
sns-linux:
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 make sns
sns-image:
- docker build -t $(IMAGE_PREFIX)aws-sns-gateway:$(IMAGE_TAG) -f ./gateways/community/aws-sns/Dockerfile .
+ docker build -t $(IMAGE_PREFIX)aws-sns-gateway:$(IMAGE_TAG) -f ./gateways/server/aws-sns/Dockerfile .
@if [ "$(DOCKER_PUSH)" = "true" ] ; then docker push $(IMAGE_PREFIX)aws-sns-gateway:$(IMAGE_TAG) ; fi
pubsub:
- go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/gcp-pubsub-gateway ./gateways/community/gcp-pubsub/cmd
+ go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/gcp-pubsub-gateway ./gateways/server/gcp-pubsub/cmd
pubsub-linux:
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 make pubsub
pubsub-image: pubsub-linux
- docker build -t $(IMAGE_PREFIX)gcp-pubsub-gateway:$(IMAGE_TAG) -f ./gateways/community/gcp-pubsub/Dockerfile .
+ docker build -t $(IMAGE_PREFIX)gcp-pubsub-gateway:$(IMAGE_TAG) -f ./gateways/server/gcp-pubsub/Dockerfile .
@if [ "$(DOCKER_PUSH)" = "true" ] ; then docker push $(IMAGE_PREFIX)gcp-pubsub-gateway:$(IMAGE_TAG) ; fi
hdfs:
- go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/hdfs-gateway ./gateways/community/hdfs/cmd
+ go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/hdfs-gateway ./gateways/server/hdfs/cmd
hdfs-linux:
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 make hdfs
hdfs-image: hdfs-linux
- docker build -t $(IMAGE_PREFIX)hdfs-gateway:$(IMAGE_TAG) -f ./gateways/community/hdfs/Dockerfile .
+ docker build -t $(IMAGE_PREFIX)hdfs-gateway:$(IMAGE_TAG) -f ./gateways/server/hdfs/Dockerfile .
@if [ "$(DOCKER_PUSH)" = "true" ] ; then docker push $(IMAGE_PREFIX)hdfs-gateway:$(IMAGE_TAG) ; fi
sqs:
- go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/aws-sqs-gateway ./gateways/community/aws-sqs/cmd
+ go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/aws-sqs-gateway ./gateways/server/aws-sqs/cmd
sqs-linux:
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 make sqs
sqs-image: sqs-linux
- docker build -t $(IMAGE_PREFIX)aws-sqs-gateway:$(IMAGE_TAG) -f ./gateways/community/aws-sqs/Dockerfile .
+ docker build -t $(IMAGE_PREFIX)aws-sqs-gateway:$(IMAGE_TAG) -f ./gateways/server/aws-sqs/Dockerfile .
@if [ "$(DOCKER_PUSH)" = "true" ] ; then docker push $(IMAGE_PREFIX)aws-sqs-gateway:$(IMAGE_TAG) ; fi
slack:
- go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/slack-gateway ./gateways/community/slack/cmd
+ go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/slack-gateway ./gateways/server/slack/cmd
slack-linux:
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 make slack
slack-image: slack-linux
- docker build -t $(IMAGE_PREFIX)slack-gateway:$(IMAGE_TAG) -f ./gateways/community/slack/Dockerfile .
+ docker build -t $(IMAGE_PREFIX)slack-gateway:$(IMAGE_TAG) -f ./gateways/server/slack/Dockerfile .
@if [ "$(DOCKER_PUSH)" = "true" ] ; then docker push $(IMAGE_PREFIX)slack-gateway:$(IMAGE_TAG) ; fi
test:
diff --git a/README.md b/README.md
index 6a1dacdc19..12e0b386b4 100644
--- a/README.md
+++ b/README.md
@@ -28,17 +28,40 @@ and trigger Kubernetes objects after successful event dependencies resolution.
* Supports [CloudEvents](https://cloudevents.io/) for describing event data and transmission.
* Ability to manage event sources at runtime.
-## Documentation
-To learn more about Argo Events, [go to complete documentation](https://argoproj.github.io/argo-events/)
+## Getting Started
+Follow [setup](https://argoproj.github.io/argo-events/installation/) instructions for installation. To see the Argo-Events in action, follow the
+[getting started](https://argoproj.github.io/argo-events/getting_started/) guide.
+Complete documentation is available at https://argoproj.github.io/argo-events/
+
+[![asciicast](https://asciinema.org/a/AKkYwzEakSUsLqH8mMORA4kza.png)](https://asciinema.org/a/AKkYwzEakSUsLqH8mMORA4kza)
+
+## Available Event Listeners
+1. AMQP
+2. AWS SNS
+3. AWS SQS
+4. Cron Schedules
+5. GCP PubSub
+6. GitHub
+7. GitLab
+8. HDFS
+9. File Based Events
+10. Kafka
+11. Minio
+12. NATS
+13. MQTT
+14. K8s Resources
+15. Slack
+16. NetApp StorageGrid
+17. Webhooks
## Who uses Argo Events?
Organizations below are **officially** using Argo Events. Please send a PR with your organization name if you are using Argo Events.
+* [BioBox Analytics](https://biobox.io)
* [BlackRock](https://www.blackrock.com/)
* [Canva](https://www.canva.com/)
* [Fairwinds](https://fairwinds.com/)
* [Intuit](https://www.intuit.com/)
* [Viaduct](https://www.viaduct.ai/)
-* [BioBox Analytics](https://biobox.io)
## Contribute
Read and abide by the [Argo Events Code of Conduct](https://github.com/argoproj/argo-events/blob/master/CODE_OF_CONDUCT.md)
diff --git a/VERSION b/VERSION
index 0eb41820ee..5473372f79 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-0.11
\ No newline at end of file
+0.12-rc
\ No newline at end of file
diff --git a/api/event-source.html b/api/event-source.html
new file mode 100644
index 0000000000..0ffead5d76
--- /dev/null
+++ b/api/event-source.html
@@ -0,0 +1,1755 @@
+Packages:
+
+argoproj.io/v1alpha1
+
+
Package v1alpha1 is the v1alpha1 version of the API.
+
+Resource Types:
+
+AMQPEventSource
+
+
+(Appears on:
+EventSourceSpec)
+
+
+
AMQPEventSource refers to an event-source for AMQP stream events
+
+
+
+
+Field |
+Description |
+
+
+
+
+
+url
+
+string
+
+ |
+
+ URL for rabbitmq service
+ |
+
+
+
+exchangeName
+
+string
+
+ |
+
+ ExchangeName is the exchange name
+For more information, visit https://www.rabbitmq.com/tutorials/amqp-concepts.html
+ |
+
+
+
+exchangeType
+
+string
+
+ |
+
+ ExchangeType is rabbitmq exchange type
+ |
+
+
+
+routingKey
+
+string
+
+ |
+
+ Routing key for bindings
+ |
+
+
+
+connectionBackoff
+
+github.com/argoproj/argo-events/common.Backoff
+
+ |
+
+(Optional)
+ Backoff holds parameters applied to connection.
+ |
+
+
+
+CalendarEventSource
+
+
+(Appears on:
+EventSourceSpec)
+
+
+
CalendarEventSource describes a time based dependency. One of the fields (schedule, interval, or recurrence) must be passed.
+Schedule takes precedence over interval; interval takes precedence over recurrence
+
+
+
+
+Field |
+Description |
+
+
+
+
+
+schedule
+
+string
+
+ |
+
+ Schedule is a cron-like expression. For reference, see: https://en.wikipedia.org/wiki/Cron
+ |
+
+
+
+interval
+
+string
+
+ |
+
+ Interval is a string that describes an interval duration, e.g. 1s, 30m, 2h…
+ |
+
+
+
+exclusionDates
+
+[]string
+
+ |
+
+ ExclusionDates defines the list of DATE-TIME exceptions for recurring events.
+ |
+
+
+
+timezone
+
+string
+
+ |
+
+(Optional)
+ Timezone in which to run the schedule
+ |
+
+
+
+userPayload
+
+encoding/json.RawMessage
+
+ |
+
+(Optional)
+ UserPayload will be sent to sensor as extra data once the event is triggered
+ |
+
+
+
+EventSource
+
+
+
EventSource is the definition of a eventsource resource
+
+
+EventSourceSpec
+
+
+(Appears on:
+EventSource)
+
+
+
EventSourceSpec refers to specification of event-source resource
+
+
+EventSourceStatus
+
+
+(Appears on:
+EventSource)
+
+
+
EventSourceStatus holds the status of the event-source resource
+
+
+FileEventSource
+
+
+(Appears on:
+EventSourceSpec)
+
+
+
FileEventSource describes an event-source for file related events.
+
+
+
+
+Field |
+Description |
+
+
+
+
+
+eventType
+
+string
+
+ |
+
+ Type of file operations to watch
+Refer https://github.com/fsnotify/fsnotify/blob/master/fsnotify.go for more information
+ |
+
+
+
+watchPathConfig
+
+github.com/argoproj/argo-events/gateways/server/common/fsevent.WatchPathConfig
+
+ |
+
+ WatchPathConfig contains configuration about the file path to watch
+ |
+
+
+
+GithubEventSource
+
+
+(Appears on:
+EventSourceSpec)
+
+
+
GithubEventSource refers to event-source for github related events
+
+
+
+
+Field |
+Description |
+
+
+
+
+
+id
+
+int64
+
+ |
+
+ Id is the webhook’s id
+ |
+
+
+
+webhook
+
+github.com/argoproj/argo-events/gateways/server/common/webhook.Context
+
+ |
+
+ Webhook refers to the configuration required to run a http server
+ |
+
+
+
+owner
+
+string
+
+ |
+
+ Owner refers to GitHub owner name i.e. argoproj
+ |
+
+
+
+repository
+
+string
+
+ |
+
+ Repository refers to GitHub repo name i.e. argo-events
+ |
+
+
+
+events
+
+[]string
+
+ |
+
+ Events refer to Github events to subscribe to which the gateway will subscribe
+ |
+
+
+
+apiToken
+
+
+Kubernetes core/v1.SecretKeySelector
+
+
+ |
+
+ APIToken refers to a K8s secret containing github api token
+ |
+
+
+
+webhookSecret
+
+
+Kubernetes core/v1.SecretKeySelector
+
+
+ |
+
+(Optional)
+ WebhookSecret refers to K8s secret containing GitHub webhook secret
+https://developer.github.com/webhooks/securing/
+ |
+
+
+
+insecure
+
+bool
+
+ |
+
+ Insecure tls verification
+ |
+
+
+
+active
+
+bool
+
+ |
+
+(Optional)
+ Active refers to status of the webhook for event deliveries.
+https://developer.github.com/webhooks/creating/#active
+ |
+
+
+
+contentType
+
+string
+
+ |
+
+ ContentType of the event delivery
+ |
+
+
+
+githubBaseURL
+
+string
+
+ |
+
+(Optional)
+ GitHub base URL (for GitHub Enterprise)
+ |
+
+
+
+githubUploadURL
+
+string
+
+ |
+
+(Optional)
+ GitHub upload URL (for GitHub Enterprise)
+ |
+
+
+
+namespace
+
+string
+
+ |
+
+ Namespace refers to Kubernetes namespace which is used to retrieve webhook secret and api token from.
+ |
+
+
+
+deleteHookOnFinish
+
+bool
+
+ |
+
+(Optional)
+ DeleteHookOnFinish determines whether to delete the GitHub hook for the repository once the event source is stopped.
+ |
+
+
+
+GitlabEventSource
+
+
+(Appears on:
+EventSourceSpec)
+
+
+
GitlabEventSource refers to event-source related to Gitlab events
+
+
+
+
+Field |
+Description |
+
+
+
+
+
+webhook
+
+github.com/argoproj/argo-events/gateways/server/common/webhook.Context
+
+ |
+
+ Webhook holds configuration to run a http server
+ |
+
+
+
+projectId
+
+string
+
+ |
+
+ ProjectId is the id of project for which integration needs to setup
+ |
+
+
+
+event
+
+string
+
+ |
+
+ Event is a gitlab event to listen to.
+Refer https://github.com/xanzy/go-gitlab/blob/bf34eca5d13a9f4c3f501d8a97b8ac226d55e4d9/projects.go#L794.
+ |
+
+
+
+accessToken
+
+
+Kubernetes core/v1.SecretKeySelector
+
+
+ |
+
+ AccessToken is reference to k8 secret which holds the gitlab api access information
+ |
+
+
+
+enableSSLVerification
+
+bool
+
+ |
+
+(Optional)
+ EnableSSLVerification to enable ssl verification
+ |
+
+
+
+gitlabBaseURL
+
+string
+
+ |
+
+ GitlabBaseURL is the base URL for API requests to a custom endpoint
+ |
+
+
+
+namespace
+
+string
+
+ |
+
+ Namespace refers to Kubernetes namespace which is used to retrieve access token from.
+ |
+
+
+
+deleteHookOnFinish
+
+bool
+
+ |
+
+(Optional)
+ DeleteHookOnFinish determines whether to delete the GitLab hook for the project once the event source is stopped.
+ |
+
+
+
+HDFSEventSource
+
+
+(Appears on:
+EventSourceSpec)
+
+
+
HDFSEventSource refers to event-source for HDFS related events
+
+
+
+
+Field |
+Description |
+
+
+
+
+
+WatchPathConfig
+
+github.com/argoproj/argo-events/gateways/server/common/fsevent.WatchPathConfig
+
+ |
+
+
+(Members of WatchPathConfig are embedded into this type.)
+
+ |
+
+
+
+type
+
+string
+
+ |
+
+ Type of file operations to watch
+ |
+
+
+
+checkInterval
+
+string
+
+ |
+
+ CheckInterval is a string that describes an interval duration to check the directory state, e.g. 1s, 30m, 2h… (defaults to 1m)
+ |
+
+
+
+addresses
+
+[]string
+
+ |
+
+ Addresses is accessible addresses of HDFS name nodes
+ |
+
+
+
+hdfsUser
+
+string
+
+ |
+
+ HDFSUser is the user to access HDFS file system.
+It is ignored if either ccache or keytab is used.
+ |
+
+
+
+krbCCacheSecret
+
+
+Kubernetes core/v1.SecretKeySelector
+
+
+ |
+
+ KrbCCacheSecret is the secret selector for Kerberos ccache
+Either ccache or keytab can be set to use Kerberos.
+ |
+
+
+
+krbKeytabSecret
+
+
+Kubernetes core/v1.SecretKeySelector
+
+
+ |
+
+ KrbKeytabSecret is the secret selector for Kerberos keytab
+Either ccache or keytab can be set to use Kerberos.
+ |
+
+
+
+krbUsername
+
+string
+
+ |
+
+ KrbUsername is the Kerberos username used with Kerberos keytab
+It must be set if keytab is used.
+ |
+
+
+
+krbRealm
+
+string
+
+ |
+
+ KrbRealm is the Kerberos realm used with Kerberos keytab
+It must be set if keytab is used.
+ |
+
+
+
+krbConfigConfigMap
+
+
+Kubernetes core/v1.ConfigMapKeySelector
+
+
+ |
+
+ KrbConfig is the configmap selector for Kerberos config as string
+It must be set if either ccache or keytab is used.
+ |
+
+
+
+krbServicePrincipalName
+
+string
+
+ |
+
+ KrbServicePrincipalName is the principal name of Kerberos service
+It must be set if either ccache or keytab is used.
+ |
+
+
+
+namespace
+
+string
+
+ |
+
+ Namespace refers to Kubernetes namespace which is used to retrieve cache secret and ket tab secret from.
+ |
+
+
+
+KafkaEventSource
+
+
+(Appears on:
+EventSourceSpec)
+
+
+
KafkaEventSource refers to event-source for Kafka related events
+
+
+
+
+Field |
+Description |
+
+
+
+
+
+url
+
+string
+
+ |
+
+ URL to kafka cluster
+ |
+
+
+
+partition
+
+string
+
+ |
+
+ Partition name
+ |
+
+
+
+topic
+
+string
+
+ |
+
+ Topic name
+ |
+
+
+
+connectionBackoff
+
+github.com/argoproj/argo-events/common.Backoff
+
+ |
+
+ Backoff holds parameters applied to connection.
+ |
+
+
+
+MQTTEventSource
+
+
+(Appears on:
+EventSourceSpec)
+
+
+
MQTTEventSource refers to event-source for MQTT related events
+
+
+
+
+Field |
+Description |
+
+
+
+
+
+url
+
+string
+
+ |
+
+ URL to connect to broker
+ |
+
+
+
+topic
+
+string
+
+ |
+
+ Topic name
+ |
+
+
+
+clientId
+
+string
+
+ |
+
+ ClientID is the id of the client
+ |
+
+
+
+connectionBackoff
+
+github.com/argoproj/argo-events/common.Backoff
+
+ |
+
+ ConnectionBackoff holds backoff applied to connection.
+ |
+
+
+
+NATSEventsSource
+
+
+(Appears on:
+EventSourceSpec)
+
+
+
NATSEventSource refers to event-source for NATS related events
+
+
+
+
+Field |
+Description |
+
+
+
+
+
+url
+
+string
+
+ |
+
+ URL to connect to NATS cluster
+ |
+
+
+
+subject
+
+string
+
+ |
+
+ Subject holds the name of the subject onto which messages are published
+ |
+
+
+
+connectionBackoff
+
+github.com/argoproj/argo-events/common.Backoff
+
+ |
+
+ ConnectionBackoff holds backoff applied to connection.
+ |
+
+
+
+PubSubEventSource
+
+
+(Appears on:
+EventSourceSpec)
+
+
+
PubSubEventSource refers to event-source for GCP PubSub related events.
+
+
+
+
+Field |
+Description |
+
+
+
+
+
+projectID
+
+string
+
+ |
+
+ ProjectID is the unique identifier for your project on GCP
+ |
+
+
+
+topicProjectID
+
+string
+
+ |
+
+ TopicProjectID identifies the project where the topic should exist or be created
+(assumed to be the same as ProjectID by default)
+ |
+
+
+
+topic
+
+string
+
+ |
+
+ Topic on which a subscription will be created
+ |
+
+
+
+credentialsFile
+
+string
+
+ |
+
+ CredentialsFile is the file that contains credentials to authenticate for GCP
+ |
+
+
+
+deleteSubscriptionOnFinish
+
+bool
+
+ |
+
+(Optional)
+ DeleteSubscriptionOnFinish determines whether to delete the GCP PubSub subscription once the event source is stopped.
+ |
+
+
+
+ResourceEventSource
+
+
+(Appears on:
+EventSourceSpec)
+
+
+
ResourceEventSource refers to a event-source for K8s resource related events.
+
+
+
+
+Field |
+Description |
+
+
+
+
+
+namespace
+
+string
+
+ |
+
+ Namespace where resource is deployed
+ |
+
+
+
+filter
+
+
+ResourceFilter
+
+
+ |
+
+(Optional)
+ Filter is applied on the metadata of the resource
+ |
+
+
+
+GroupVersionResource
+
+
+Kubernetes meta/v1.GroupVersionResource
+
+
+ |
+
+
+(Members of GroupVersionResource are embedded into this type.)
+
+Group of the resource
+ |
+
+
+
+eventType
+
+
+ResourceEventType
+
+
+ |
+
+(Optional)
+ Type is the event type.
+If not provided, the gateway will watch all events for a resource.
+ |
+
+
+
+ResourceEventType
+(string
alias)
+
+(Appears on:
+ResourceEventSource)
+
+
+
ResourceEventType is the type of event for the K8s resource mutation
+
+ResourceFilter
+
+
+(Appears on:
+ResourceEventSource)
+
+
+
ResourceFilter contains K8 ObjectMeta information to further filter resource event objects
+
+
+
+
+Field |
+Description |
+
+
+
+
+
+prefix
+
+string
+
+ |
+
+(Optional)
+ |
+
+
+
+labels
+
+map[string]string
+
+ |
+
+(Optional)
+ |
+
+
+
+fields
+
+map[string]string
+
+ |
+
+(Optional)
+ |
+
+
+
+createdBy
+
+
+Kubernetes meta/v1.Time
+
+
+ |
+
+(Optional)
+ |
+
+
+
+SNSEventSource
+
+
+(Appears on:
+EventSourceSpec)
+
+
+
SNSEventSource refers to event-source for AWS SNS related events
+
+
+
+
+Field |
+Description |
+
+
+
+
+
+webhook
+
+github.com/argoproj/argo-events/gateways/server/common/webhook.Context
+
+ |
+
+ Webhook configuration for http server
+ |
+
+
+
+topicArn
+
+string
+
+ |
+
+ TopicArn
+ |
+
+
+
+accessKey
+
+
+Kubernetes core/v1.SecretKeySelector
+
+
+ |
+
+ AccessKey refers K8 secret containing aws access key
+ |
+
+
+
+secretKey
+
+
+Kubernetes core/v1.SecretKeySelector
+
+
+ |
+
+ SecretKey refers K8 secret containing aws secret key
+ |
+
+
+
+namespace
+
+string
+
+ |
+
+(Optional)
+ Namespace refers to Kubernetes namespace to read access related secret from.
+ |
+
+
+
+region
+
+string
+
+ |
+
+ Region is AWS region
+ |
+
+
+
+SQSEventSource
+
+
+(Appears on:
+EventSourceSpec)
+
+
+
SQSEventSource refers to event-source for AWS SQS related events
+
+
+
+
+Field |
+Description |
+
+
+
+
+
+accessKey
+
+
+Kubernetes core/v1.SecretKeySelector
+
+
+ |
+
+ AccessKey refers K8 secret containing aws access key
+ |
+
+
+
+secretKey
+
+
+Kubernetes core/v1.SecretKeySelector
+
+
+ |
+
+ SecretKey refers K8 secret containing aws secret key
+ |
+
+
+
+region
+
+string
+
+ |
+
+ Region is AWS region
+ |
+
+
+
+queue
+
+string
+
+ |
+
+ Queue is AWS SQS queue to listen to for messages
+ |
+
+
+
+waitTimeSeconds
+
+int64
+
+ |
+
+ WaitTimeSeconds is The duration (in seconds) for which the call waits for a message to arrive
+in the queue before returning.
+ |
+
+
+
+namespace
+
+string
+
+ |
+
+(Optional)
+ Namespace refers to Kubernetes namespace to read access related secret from.
+ |
+
+
+
+SlackEventSource
+
+
+(Appears on:
+EventSourceSpec)
+
+
+
SlackEventSource refers to event-source for Slack related events
+
+
+
+
+Field |
+Description |
+
+
+
+
+
+signingSecret
+
+
+Kubernetes core/v1.SecretKeySelector
+
+
+ |
+
+ Slack App signing secret
+ |
+
+
+
+token
+
+
+Kubernetes core/v1.SecretKeySelector
+
+
+ |
+
+ Token for URL verification handshake
+ |
+
+
+
+webhook
+
+github.com/argoproj/argo-events/gateways/server/common/webhook.Context
+
+ |
+
+ Webhook holds configuration for a REST endpoint
+ |
+
+
+
+namespace
+
+string
+
+ |
+
+ Namespace refers to Kubernetes namespace which is used to retrieve token and signing secret from.
+ |
+
+
+
+StorageGridEventSource
+
+
+(Appears on:
+EventSourceSpec)
+
+
+
StorageGridEventSource refers to event-source for StorageGrid related events
+
+
+StorageGridFilter
+
+
+(Appears on:
+StorageGridEventSource)
+
+
+
Filter represents filters to apply to bucket notifications for specifying constraints on objects
+
+
+
+
+Field |
+Description |
+
+
+
+
+
+prefix
+
+string
+
+ |
+
+ |
+
+
+
+suffix
+
+string
+
+ |
+
+ |
+
+
+
+
+
+Generated with gen-crd-api-reference-docs
+on git commit 8d85191
.
+
diff --git a/api/event-source.md b/api/event-source.md
new file mode 100644
index 0000000000..06589b34aa
--- /dev/null
+++ b/api/event-source.md
@@ -0,0 +1,3489 @@
+
+
+Packages:
+
+
+
+
+
+
+
+argoproj.io/v1alpha1
+
+
+
+
+
+
+
+Package v1alpha1 is the v1alpha1 version of the API.
+
+
+
+
+
+Resource Types:
+
+
+
+
+
+AMQPEventSource
+
+
+
+
+
+(Appears on:
+EventSourceSpec)
+
+
+
+
+
+
+
+AMQPEventSource refers to an event-source for AMQP stream events
+
+
+
+
+
+
+
+
+
+
+
+
+
+Field
+
+ |
+
+
+
+Description
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+url string
+
+ |
+
+
+
+
+
+URL for rabbitmq service
+
+
+
+ |
+
+
+
+
+
+
+
+exchangeName string
+
+ |
+
+
+
+
+
+ExchangeName is the exchange name For more information, visit
+https://www.rabbitmq.com/tutorials/amqp-concepts.html
+
+
+
+ |
+
+
+
+
+
+
+
+exchangeType string
+
+ |
+
+
+
+
+
+ExchangeType is rabbitmq exchange type
+
+
+
+ |
+
+
+
+
+
+
+
+routingKey string
+
+ |
+
+
+
+
+
+Routing key for bindings
+
+
+
+ |
+
+
+
+
+
+
+
+connectionBackoff
+github.com/argoproj/argo-events/common.Backoff
+
+ |
+
+
+
+(Optional)
+
+
+
+Backoff holds parameters applied to connection.
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+CalendarEventSource
+
+
+
+
+
+(Appears on:
+EventSourceSpec)
+
+
+
+
+
+
+
+CalendarEventSource describes a time based dependency. One of the fields
+(schedule, interval, or recurrence) must be passed. Schedule takes
+precedence over interval; interval takes precedence over recurrence
+
+
+
+
+
+
+
+
+
+
+
+
+
+Field
+
+ |
+
+
+
+Description
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+schedule string
+
+ |
+
+
+
+
+
+Schedule is a cron-like expression. For reference, see:
+https://en.wikipedia.org/wiki/Cron
+
+
+
+ |
+
+
+
+
+
+
+
+interval string
+
+ |
+
+
+
+
+
+Interval is a string that describes an interval duration, e.g. 1s, 30m,
+2h…
+
+
+
+ |
+
+
+
+
+
+
+
+exclusionDates \[\]string
+
+ |
+
+
+
+
+
+ExclusionDates defines the list of DATE-TIME exceptions for recurring
+events.
+
+
+
+ |
+
+
+
+
+
+
+
+timezone string
+
+ |
+
+
+
+(Optional)
+
+
+
+Timezone in which to run the schedule
+
+
+
+ |
+
+
+
+
+
+
+
+userPayload encoding/json.RawMessage
+
+ |
+
+
+
+(Optional)
+
+
+
+UserPayload will be sent to sensor as extra data once the event is
+triggered
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+EventSource
+
+
+
+
+
+
+
+EventSource is the definition of a eventsource resource
+
+
+
+
+
+
+
+
+
+
+
+
+
+Field
+
+ |
+
+
+
+Description
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+metadata
+
+Kubernetes meta/v1.ObjectMeta
+
+ |
+
+
+
+Refer to the Kubernetes API documentation for the fields of the
+metadata field.
+
+ |
+
+
+
+
+
+
+
+status
+ EventSourceStatus
+
+
+ |
+
+
+
+ |
+
+
+
+
+
+
+
+spec
+ EventSourceSpec
+
+
+ |
+
+
+
+
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+EventSourceSpec
+
+
+
+
+
+(Appears on:
+EventSource)
+
+
+
+
+
+
+
+EventSourceSpec refers to specification of event-source resource
+
+
+
+
+
+
+
+
+
+EventSourceStatus
+
+
+
+
+
+(Appears on:
+EventSource)
+
+
+
+
+
+
+
+EventSourceStatus holds the status of the event-source resource
+
+
+
+
+
+
+
+
+
+
+
+
+
+Field
+
+ |
+
+
+
+Description
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+createdAt
+
+Kubernetes meta/v1.Time
+
+ |
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+FileEventSource
+
+
+
+
+
+(Appears on:
+EventSourceSpec)
+
+
+
+
+
+
+
+FileEventSource describes an event-source for file related events.
+
+
+
+
+
+
+
+
+
+
+
+
+
+Field
+
+ |
+
+
+
+Description
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+eventType string
+
+ |
+
+
+
+
+
+Type of file operations to watch Refer
+https://github.com/fsnotify/fsnotify/blob/master/fsnotify.go
+for more information
+
+
+
+ |
+
+
+
+
+
+
+
+watchPathConfig
+github.com/argoproj/argo-events/gateways/server/common/fsevent.WatchPathConfig
+
+
+ |
+
+
+
+
+
+WatchPathConfig contains configuration about the file path to watch
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+GithubEventSource
+
+
+
+
+
+(Appears on:
+EventSourceSpec)
+
+
+
+
+
+
+
+GithubEventSource refers to event-source for github related events
+
+
+
+
+
+
+
+
+
+
+
+
+
+Field
+
+ |
+
+
+
+Description
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+id int64
+
+ |
+
+
+
+
+
+Id is the webhook’s id
+
+
+
+ |
+
+
+
+
+
+
+
+webhook
+github.com/argoproj/argo-events/gateways/server/common/webhook.Context
+
+
+ |
+
+
+
+
+
+Webhook refers to the configuration required to run a http server
+
+
+
+ |
+
+
+
+
+
+
+
+owner string
+
+ |
+
+
+
+
+
+Owner refers to GitHub owner name i.e. argoproj
+
+
+
+ |
+
+
+
+
+
+
+
+repository string
+
+ |
+
+
+
+
+
+Repository refers to GitHub repo name i.e. argo-events
+
+
+
+ |
+
+
+
+
+
+
+
+events \[\]string
+
+ |
+
+
+
+
+
+Events refer to Github events to subscribe to which the gateway will
+subscribe
+
+
+
+ |
+
+
+
+
+
+
+
+apiToken
+
+Kubernetes core/v1.SecretKeySelector
+
+ |
+
+
+
+
+
+APIToken refers to a K8s secret containing github api token
+
+
+
+ |
+
+
+
+
+
+
+
+webhookSecret
+
+Kubernetes core/v1.SecretKeySelector
+
+ |
+
+
+
+(Optional)
+
+
+
+WebhookSecret refers to K8s secret containing GitHub webhook secret
+https://developer.github.com/webhooks/securing/
+
+
+
+ |
+
+
+
+
+
+
+
+insecure bool
+
+ |
+
+
+
+
+
+Insecure tls verification
+
+
+
+ |
+
+
+
+
+
+
+
+active bool
+
+ |
+
+
+
+(Optional)
+
+
+
+Active refers to status of the webhook for event deliveries.
+https://developer.github.com/webhooks/creating/\#active
+
+
+
+ |
+
+
+
+
+
+
+
+contentType string
+
+ |
+
+
+
+
+
+ContentType of the event delivery
+
+
+
+ |
+
+
+
+
+
+
+
+githubBaseURL string
+
+ |
+
+
+
+(Optional)
+
+
+
+GitHub base URL (for GitHub Enterprise)
+
+
+
+ |
+
+
+
+
+
+
+
+githubUploadURL string
+
+ |
+
+
+
+(Optional)
+
+
+
+GitHub upload URL (for GitHub Enterprise)
+
+
+
+ |
+
+
+
+
+
+
+
+namespace string
+
+ |
+
+
+
+
+
+Namespace refers to Kubernetes namespace which is used to retrieve
+webhook secret and api token from.
+
+
+
+ |
+
+
+
+
+
+
+
+deleteHookOnFinish bool
+
+ |
+
+
+
+(Optional)
+
+
+
+DeleteHookOnFinish determines whether to delete the GitHub hook for the
+repository once the event source is stopped.
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+GitlabEventSource
+
+
+
+
+
+(Appears on:
+EventSourceSpec)
+
+
+
+
+
+
+
+GitlabEventSource refers to event-source related to Gitlab events
+
+
+
+
+
+
+
+
+
+
+
+
+
+Field
+
+ |
+
+
+
+Description
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+webhook
+github.com/argoproj/argo-events/gateways/server/common/webhook.Context
+
+
+ |
+
+
+
+
+
+Webhook holds configuration to run a http server
+
+
+
+ |
+
+
+
+
+
+
+
+projectId string
+
+ |
+
+
+
+
+
+ProjectId is the id of project for which integration needs to setup
+
+
+
+ |
+
+
+
+
+
+
+
+event string
+
+ |
+
+
+
+
+
+Event is a gitlab event to listen to. Refer
+https://github.com/xanzy/go-gitlab/blob/bf34eca5d13a9f4c3f501d8a97b8ac226d55e4d9/projects.go\#L794.
+
+
+
+ |
+
+
+
+
+
+
+
+accessToken
+
+Kubernetes core/v1.SecretKeySelector
+
+ |
+
+
+
+
+
+AccessToken is reference to k8 secret which holds the gitlab api access
+information
+
+
+
+ |
+
+
+
+
+
+
+
+enableSSLVerification bool
+
+ |
+
+
+
+(Optional)
+
+
+
+EnableSSLVerification to enable ssl verification
+
+
+
+ |
+
+
+
+
+
+
+
+gitlabBaseURL string
+
+ |
+
+
+
+
+
+GitlabBaseURL is the base URL for API requests to a custom endpoint
+
+
+
+ |
+
+
+
+
+
+
+
+namespace string
+
+ |
+
+
+
+
+
+Namespace refers to Kubernetes namespace which is used to retrieve
+access token from.
+
+
+
+ |
+
+
+
+
+
+
+
+deleteHookOnFinish bool
+
+ |
+
+
+
+(Optional)
+
+
+
+DeleteHookOnFinish determines whether to delete the GitLab hook for the
+project once the event source is stopped.
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+HDFSEventSource
+
+
+
+
+
+(Appears on:
+EventSourceSpec)
+
+
+
+
+
+
+
+HDFSEventSource refers to event-source for HDFS related events
+
+
+
+
+
+
+
+
+
+
+
+
+
+Field
+
+ |
+
+
+
+Description
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+WatchPathConfig
+github.com/argoproj/argo-events/gateways/server/common/fsevent.WatchPathConfig
+
+
+ |
+
+
+
+
+
+(Members of WatchPathConfig are embedded into this type.)
+
+
+
+ |
+
+
+
+
+
+
+
+type string
+
+ |
+
+
+
+
+
+Type of file operations to watch
+
+
+
+ |
+
+
+
+
+
+
+
+checkInterval string
+
+ |
+
+
+
+
+
+CheckInterval is a string that describes an interval duration to check
+the directory state, e.g. 1s, 30m, 2h… (defaults to 1m)
+
+
+
+ |
+
+
+
+
+
+
+
+addresses \[\]string
+
+ |
+
+
+
+
+
+Addresses is accessible addresses of HDFS name nodes
+
+
+
+ |
+
+
+
+
+
+
+
+hdfsUser string
+
+ |
+
+
+
+
+
+HDFSUser is the user to access HDFS file system. It is ignored if either
+ccache or keytab is used.
+
+
+
+ |
+
+
+
+
+
+
+
+krbCCacheSecret
+
+Kubernetes core/v1.SecretKeySelector
+
+ |
+
+
+
+
+
+KrbCCacheSecret is the secret selector for Kerberos ccache Either ccache
+or keytab can be set to use Kerberos.
+
+
+
+ |
+
+
+
+
+
+
+
+krbKeytabSecret
+
+Kubernetes core/v1.SecretKeySelector
+
+ |
+
+
+
+
+
+KrbKeytabSecret is the secret selector for Kerberos keytab Either ccache
+or keytab can be set to use Kerberos.
+
+
+
+ |
+
+
+
+
+
+
+
+krbUsername string
+
+ |
+
+
+
+
+
+KrbUsername is the Kerberos username used with Kerberos keytab It must
+be set if keytab is used.
+
+
+
+ |
+
+
+
+
+
+
+
+krbRealm string
+
+ |
+
+
+
+
+
+KrbRealm is the Kerberos realm used with Kerberos keytab It must be set
+if keytab is used.
+
+
+
+ |
+
+
+
+
+
+
+
+krbConfigConfigMap
+
+Kubernetes core/v1.ConfigMapKeySelector
+
+ |
+
+
+
+
+
+KrbConfig is the configmap selector for Kerberos config as string It
+must be set if either ccache or keytab is used.
+
+
+
+ |
+
+
+
+
+
+
+
+krbServicePrincipalName string
+
+ |
+
+
+
+
+
+KrbServicePrincipalName is the principal name of Kerberos service It
+must be set if either ccache or keytab is used.
+
+
+
+ |
+
+
+
+
+
+
+
+namespace string
+
+ |
+
+
+
+
+
+Namespace refers to Kubernetes namespace which is used to retrieve cache
+secret and ket tab secret from.
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+KafkaEventSource
+
+
+
+
+
+(Appears on:
+EventSourceSpec)
+
+
+
+
+
+
+
+KafkaEventSource refers to event-source for Kafka related events
+
+
+
+
+
+
+
+
+
+
+
+
+
+Field
+
+ |
+
+
+
+Description
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+url string
+
+ |
+
+
+
+
+
+URL to kafka cluster
+
+
+
+ |
+
+
+
+
+
+
+
+partition string
+
+ |
+
+
+
+
+
+Partition name
+
+
+
+ |
+
+
+
+
+
+
+
+topic string
+
+ |
+
+
+
+
+
+Topic name
+
+
+
+ |
+
+
+
+
+
+
+
+connectionBackoff
+github.com/argoproj/argo-events/common.Backoff
+
+ |
+
+
+
+
+
+Backoff holds parameters applied to connection.
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+MQTTEventSource
+
+
+
+
+
+(Appears on:
+EventSourceSpec)
+
+
+
+
+
+
+
+MQTTEventSource refers to event-source for MQTT related events
+
+
+
+
+
+
+
+
+
+
+
+
+
+Field
+
+ |
+
+
+
+Description
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+url string
+
+ |
+
+
+
+
+
+URL to connect to broker
+
+
+
+ |
+
+
+
+
+
+
+
+topic string
+
+ |
+
+
+
+
+
+Topic name
+
+
+
+ |
+
+
+
+
+
+
+
+clientId string
+
+ |
+
+
+
+
+
+ClientID is the id of the client
+
+
+
+ |
+
+
+
+
+
+
+
+connectionBackoff
+github.com/argoproj/argo-events/common.Backoff
+
+ |
+
+
+
+
+
+ConnectionBackoff holds backoff applied to connection.
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+NATSEventsSource
+
+
+
+
+
+(Appears on:
+EventSourceSpec)
+
+
+
+
+
+
+
+NATSEventSource refers to event-source for NATS related events
+
+
+
+
+
+
+
+
+
+
+
+
+
+Field
+
+ |
+
+
+
+Description
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+url string
+
+ |
+
+
+
+
+
+URL to connect to NATS cluster
+
+
+
+ |
+
+
+
+
+
+
+
+subject string
+
+ |
+
+
+
+
+
+Subject holds the name of the subject onto which messages are published
+
+
+
+ |
+
+
+
+
+
+
+
+connectionBackoff
+github.com/argoproj/argo-events/common.Backoff
+
+ |
+
+
+
+
+
+ConnectionBackoff holds backoff applied to connection.
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+PubSubEventSource
+
+
+
+
+
+(Appears on:
+EventSourceSpec)
+
+
+
+
+
+
+
+PubSubEventSource refers to event-source for GCP PubSub related events.
+
+
+
+
+
+
+
+
+
+
+
+
+
+Field
+
+ |
+
+
+
+Description
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+projectID string
+
+ |
+
+
+
+
+
+ProjectID is the unique identifier for your project on GCP
+
+
+
+ |
+
+
+
+
+
+
+
+topicProjectID string
+
+ |
+
+
+
+
+
+TopicProjectID identifies the project where the topic should exist or be
+created (assumed to be the same as ProjectID by default)
+
+
+
+ |
+
+
+
+
+
+
+
+topic string
+
+ |
+
+
+
+
+
+Topic on which a subscription will be created
+
+
+
+ |
+
+
+
+
+
+
+
+credentialsFile string
+
+ |
+
+
+
+
+
+CredentialsFile is the file that contains credentials to authenticate
+for GCP
+
+
+
+ |
+
+
+
+
+
+
+
+deleteSubscriptionOnFinish bool
+
+ |
+
+
+
+(Optional)
+
+
+
+DeleteSubscriptionOnFinish determines whether to delete the GCP PubSub
+subscription once the event source is stopped.
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+ResourceEventSource
+
+
+
+
+
+(Appears on:
+EventSourceSpec)
+
+
+
+
+
+
+
+ResourceEventSource refers to a event-source for K8s resource related
+events.
+
+
+
+
+
+
+
+
+
+
+
+
+
+Field
+
+ |
+
+
+
+Description
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+namespace string
+
+ |
+
+
+
+
+
+Namespace where resource is deployed
+
+
+
+ |
+
+
+
+
+
+
+
+filter
+ ResourceFilter
+
+
+ |
+
+
+
+(Optional)
+
+
+
+Filter is applied on the metadata of the resource
+
+
+
+ |
+
+
+
+
+
+
+
+GroupVersionResource
+
+Kubernetes meta/v1.GroupVersionResource
+
+ |
+
+
+
+
+
+(Members of GroupVersionResource are embedded into this
+type.)
+
+
+
+
+
+Group of the resource
+
+
+
+ |
+
+
+
+
+
+
+
+eventType
+ ResourceEventType
+
+
+ |
+
+
+
+(Optional)
+
+
+
+Type is the event type. If not provided, the gateway will watch all
+events for a resource.
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+ResourceEventType (string
alias)
+
+
+
+
+
+
+
+(Appears on:
+ResourceEventSource)
+
+
+
+
+
+
+
+ResourceEventType is the type of event for the K8s resource mutation
+
+
+
+
+
+
+
+ResourceFilter
+
+
+
+
+
+(Appears on:
+ResourceEventSource)
+
+
+
+
+
+
+
+ResourceFilter contains K8 ObjectMeta information to further filter
+resource event objects
+
+
+
+
+
+
+
+
+
+
+
+
+
+Field
+
+ |
+
+
+
+Description
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+prefix string
+
+ |
+
+
+
+(Optional)
+
+ |
+
+
+
+
+
+
+
+labels map\[string\]string
+
+ |
+
+
+
+(Optional)
+
+ |
+
+
+
+
+
+
+
+fields map\[string\]string
+
+ |
+
+
+
+(Optional)
+
+ |
+
+
+
+
+
+
+
+createdBy
+
+Kubernetes meta/v1.Time
+
+ |
+
+
+
+(Optional)
+
+ |
+
+
+
+
+
+
+
+
+
+SNSEventSource
+
+
+
+
+
+(Appears on:
+EventSourceSpec)
+
+
+
+
+
+
+
+SNSEventSource refers to event-source for AWS SNS related events
+
+
+
+
+
+
+
+
+
+
+
+
+
+Field
+
+ |
+
+
+
+Description
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+webhook
+github.com/argoproj/argo-events/gateways/server/common/webhook.Context
+
+
+ |
+
+
+
+
+
+Webhook configuration for http server
+
+
+
+ |
+
+
+
+
+
+
+
+topicArn string
+
+ |
+
+
+
+
+
+TopicArn
+
+
+
+ |
+
+
+
+
+
+
+
+accessKey
+
+Kubernetes core/v1.SecretKeySelector
+
+ |
+
+
+
+
+
+AccessKey refers K8 secret containing aws access key
+
+
+
+ |
+
+
+
+
+
+
+
+secretKey
+
+Kubernetes core/v1.SecretKeySelector
+
+ |
+
+
+
+
+
+SecretKey refers K8 secret containing aws secret key
+
+
+
+ |
+
+
+
+
+
+
+
+namespace string
+
+ |
+
+
+
+(Optional)
+
+
+
+Namespace refers to Kubernetes namespace to read access related secret
+from.
+
+
+
+ |
+
+
+
+
+
+
+
+region string
+
+ |
+
+
+
+
+
+Region is AWS region
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+SQSEventSource
+
+
+
+
+
+(Appears on:
+EventSourceSpec)
+
+
+
+
+
+
+
+SQSEventSource refers to event-source for AWS SQS related events
+
+
+
+
+
+
+
+
+
+
+
+
+
+Field
+
+ |
+
+
+
+Description
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+accessKey
+
+Kubernetes core/v1.SecretKeySelector
+
+ |
+
+
+
+
+
+AccessKey refers K8 secret containing aws access key
+
+
+
+ |
+
+
+
+
+
+
+
+secretKey
+
+Kubernetes core/v1.SecretKeySelector
+
+ |
+
+
+
+
+
+SecretKey refers K8 secret containing aws secret key
+
+
+
+ |
+
+
+
+
+
+
+
+region string
+
+ |
+
+
+
+
+
+Region is AWS region
+
+
+
+ |
+
+
+
+
+
+
+
+queue string
+
+ |
+
+
+
+
+
+Queue is AWS SQS queue to listen to for messages
+
+
+
+ |
+
+
+
+
+
+
+
+waitTimeSeconds int64
+
+ |
+
+
+
+
+
+WaitTimeSeconds is The duration (in seconds) for which the call waits
+for a message to arrive in the queue before returning.
+
+
+
+ |
+
+
+
+
+
+
+
+namespace string
+
+ |
+
+
+
+(Optional)
+
+
+
+Namespace refers to Kubernetes namespace to read access related secret
+from.
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+SlackEventSource
+
+
+
+
+
+(Appears on:
+EventSourceSpec)
+
+
+
+
+
+
+
+SlackEventSource refers to event-source for Slack related events
+
+
+
+
+
+
+
+
+
+
+
+
+
+Field
+
+ |
+
+
+
+Description
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+signingSecret
+
+Kubernetes core/v1.SecretKeySelector
+
+ |
+
+
+
+
+
+Slack App signing secret
+
+
+
+ |
+
+
+
+
+
+
+
+token
+
+Kubernetes core/v1.SecretKeySelector
+
+ |
+
+
+
+
+
+Token for URL verification handshake
+
+
+
+ |
+
+
+
+
+
+
+
+webhook
+github.com/argoproj/argo-events/gateways/server/common/webhook.Context
+
+
+ |
+
+
+
+
+
+Webhook holds configuration for a REST endpoint
+
+
+
+ |
+
+
+
+
+
+
+
+namespace string
+
+ |
+
+
+
+
+
+Namespace refers to Kubernetes namespace which is used to retrieve token
+and signing secret from.
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+StorageGridEventSource
+
+
+
+
+
+(Appears on:
+EventSourceSpec)
+
+
+
+
+
+
+
+StorageGridEventSource refers to event-source for StorageGrid related
+events
+
+
+
+
+
+
+
+
+
+StorageGridFilter
+
+
+
+
+
+(Appears on:
+StorageGridEventSource)
+
+
+
+
+
+
+
+Filter represents filters to apply to bucket notifications for
+specifying constraints on objects
+
+
+
+
+
+
+
+
+
+
+
+
+
+Field
+
+ |
+
+
+
+Description
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+prefix string
+
+ |
+
+
+
+ |
+
+
+
+
+
+
+
+suffix string
+
+ |
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+ Generated with gen-crd-api-reference-docs
on git
+commit 8d85191
.
+
+
diff --git a/api/gateway.html b/api/gateway.html
new file mode 100644
index 0000000000..965c9ad323
--- /dev/null
+++ b/api/gateway.html
@@ -0,0 +1,731 @@
+Packages:
+
+argoproj.io/v1alpha1
+
+
Package v1alpha1 is the v1alpha1 version of the API.
+
+Resource Types:
+
+EventSourceRef
+
+
+(Appears on:
+GatewaySpec)
+
+
+
EventSourceRef holds information about the EventSourceRef custom resource
+
+
+
+
+Field |
+Description |
+
+
+
+
+
+name
+
+string
+
+ |
+
+ Name of the event source
+ |
+
+
+
+namespace
+
+string
+
+ |
+
+(Optional)
+ Namespace of the event source
+Default value is the namespace where referencing gateway is deployed
+ |
+
+
+
+Gateway
+
+
+
Gateway is the definition of a gateway resource
+
+
+
+
+Field |
+Description |
+
+
+
+
+
+metadata
+
+
+Kubernetes meta/v1.ObjectMeta
+
+
+ |
+
+Refer to the Kubernetes API documentation for the fields of the
+metadata field.
+ |
+
+
+
+status
+
+
+GatewayStatus
+
+
+ |
+
+ |
+
+
+
+spec
+
+
+GatewaySpec
+
+
+ |
+
+
+
+
+
+
+template
+
+
+Kubernetes core/v1.PodTemplateSpec
+
+
+ |
+
+ Template is the pod specification for the gateway
+Refer https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.11/#pod-v1-core
+ |
+
+
+
+eventSourceRef
+
+
+EventSourceRef
+
+
+ |
+
+ EventSourceRef refers to event-source that stores event source configurations for the gateway
+ |
+
+
+
+type
+
+Argo Events common.EventSourceType
+
+ |
+
+ Type is the type of gateway. Used as metadata.
+ |
+
+
+
+service
+
+
+Kubernetes core/v1.Service
+
+
+ |
+
+ Service is the specifications of the service to expose the gateway
+Refer https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.11/#service-v1-core
+ |
+
+
+
+watchers
+
+
+NotificationWatchers
+
+
+ |
+
+ Watchers are components which are interested listening to notifications from this gateway
+These only need to be specified when gateway dispatch mechanism is through HTTP POST notifications.
+In future, support for NATS, KAFKA will be added as a means to dispatch notifications in which case
+specifying watchers would be unnecessary.
+ |
+
+
+
+processorPort
+
+string
+
+ |
+
+ Port on which the gateway event source processor is running on.
+ |
+
+
+
+eventProtocol
+
+Argo Events common.EventProtocol
+
+ |
+
+ EventProtocol is the underlying protocol used to send events from gateway to watchers(components interested in listening to event from this gateway)
+ |
+
+
+
+replica
+
+int
+
+ |
+
+ Replica is the gateway deployment replicas
+ |
+
+
+ |
+
+
+
+GatewayNotificationWatcher
+
+
+(Appears on:
+NotificationWatchers)
+
+
+
GatewayNotificationWatcher is the gateway interested in listening to notifications from this gateway
+
+
+
+
+Field |
+Description |
+
+
+
+
+
+name
+
+string
+
+ |
+
+ Name is the gateway name
+ |
+
+
+
+port
+
+string
+
+ |
+
+ Port is http server port on which gateway is running
+ |
+
+
+
+endpoint
+
+string
+
+ |
+
+ Endpoint is REST API endpoint to post event to.
+Events are sent using HTTP POST method to this endpoint.
+ |
+
+
+
+namespace
+
+string
+
+ |
+
+ Namespace of the gateway
+ |
+
+
+
+GatewayResource
+
+
+(Appears on:
+GatewayStatus)
+
+
+
GatewayResource holds the metadata about the gateway resources
+
+
+GatewaySpec
+
+
+(Appears on:
+Gateway)
+
+
+
GatewaySpec represents gateway specifications
+
+
+
+
+Field |
+Description |
+
+
+
+
+
+template
+
+
+Kubernetes core/v1.PodTemplateSpec
+
+
+ |
+
+ Template is the pod specification for the gateway
+Refer https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.11/#pod-v1-core
+ |
+
+
+
+eventSourceRef
+
+
+EventSourceRef
+
+
+ |
+
+ EventSourceRef refers to event-source that stores event source configurations for the gateway
+ |
+
+
+
+type
+
+Argo Events common.EventSourceType
+
+ |
+
+ Type is the type of gateway. Used as metadata.
+ |
+
+
+
+service
+
+
+Kubernetes core/v1.Service
+
+
+ |
+
+ Service is the specifications of the service to expose the gateway
+Refer https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.11/#service-v1-core
+ |
+
+
+
+watchers
+
+
+NotificationWatchers
+
+
+ |
+
+ Watchers are components which are interested listening to notifications from this gateway
+These only need to be specified when gateway dispatch mechanism is through HTTP POST notifications.
+In future, support for NATS, KAFKA will be added as a means to dispatch notifications in which case
+specifying watchers would be unnecessary.
+ |
+
+
+
+processorPort
+
+string
+
+ |
+
+ Port on which the gateway event source processor is running on.
+ |
+
+
+
+eventProtocol
+
+Argo Events common.EventProtocol
+
+ |
+
+ EventProtocol is the underlying protocol used to send events from gateway to watchers(components interested in listening to event from this gateway)
+ |
+
+
+
+replica
+
+int
+
+ |
+
+ Replica is the gateway deployment replicas
+ |
+
+
+
+GatewayStatus
+
+
+(Appears on:
+Gateway)
+
+
+
GatewayStatus contains information about the status of a gateway.
+
+
+NodePhase
+(string
alias)
+
+(Appears on:
+GatewayStatus,
+NodeStatus)
+
+
+
NodePhase is the label for the condition of a node.
+
+NodeStatus
+
+
+(Appears on:
+GatewayStatus)
+
+
+
NodeStatus describes the status for an individual node in the gateway configurations.
+A single node can represent one configuration.
+
+
+
+
+Field |
+Description |
+
+
+
+
+
+id
+
+string
+
+ |
+
+ ID is a unique identifier of a node within a sensor
+It is a hash of the node name
+ |
+
+
+
+name
+
+string
+
+ |
+
+ Name is a unique name in the node tree used to generate the node ID
+ |
+
+
+
+displayName
+
+string
+
+ |
+
+ DisplayName is the human readable representation of the node
+ |
+
+
+
+phase
+
+
+NodePhase
+
+
+ |
+
+ Phase of the node
+ |
+
+
+
+startedAt
+
+
+Kubernetes meta/v1.MicroTime
+
+
+ |
+
+ StartedAt is the time at which this node started
+ |
+
+
+
+message
+
+string
+
+ |
+
+ Message store data or something to save for configuration
+ |
+
+
+
+updateTime
+
+
+Kubernetes meta/v1.MicroTime
+
+
+ |
+
+ UpdateTime is the time when node(gateway configuration) was updated
+ |
+
+
+
+NotificationWatchers
+
+
+(Appears on:
+GatewaySpec)
+
+
+
NotificationWatchers are components which are interested listening to notifications from this gateway
+
+
+
+
+Field |
+Description |
+
+
+
+
+
+gateways
+
+
+[]GatewayNotificationWatcher
+
+
+ |
+
+ Gateways is the list of gateways interested in listening to notifications from this gateway
+ |
+
+
+
+sensors
+
+
+[]SensorNotificationWatcher
+
+
+ |
+
+ Sensors is the list of sensors interested in listening to notifications from this gateway
+ |
+
+
+
+SensorNotificationWatcher
+
+
+(Appears on:
+NotificationWatchers)
+
+
+
SensorNotificationWatcher is the sensor interested in listening to notifications from this gateway
+
+
+
+
+Field |
+Description |
+
+
+
+
+
+name
+
+string
+
+ |
+
+ Name is the name of the sensor
+ |
+
+
+
+namespace
+
+string
+
+ |
+
+ Namespace of the sensor
+ |
+
+
+
+
+
+Generated with gen-crd-api-reference-docs
+on git commit 8d85191
.
+
diff --git a/api/gateway.md b/api/gateway.md
new file mode 100644
index 0000000000..20df87387d
--- /dev/null
+++ b/api/gateway.md
@@ -0,0 +1,1451 @@
+
+
+Packages:
+
+
+
+
+
+
+
+argoproj.io/v1alpha1
+
+
+
+
+
+
+
+Package v1alpha1 is the v1alpha1 version of the API.
+
+
+
+
+
+Resource Types:
+
+
+
+
+
+EventSourceRef
+
+
+
+
+
+(Appears on:
+GatewaySpec)
+
+
+
+
+
+
+
+EventSourceRef holds information about the EventSourceRef custom
+resource
+
+
+
+
+
+
+
+
+
+
+
+
+
+Field
+
+ |
+
+
+
+Description
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+name string
+
+ |
+
+
+
+
+
+Name of the event source
+
+
+
+ |
+
+
+
+
+
+
+
+namespace string
+
+ |
+
+
+
+(Optional)
+
+
+
+Namespace of the event source Default value is the namespace where
+referencing gateway is deployed
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+Gateway
+
+
+
+
+
+
+
+Gateway is the definition of a gateway resource
+
+
+
+
+
+
+
+
+
+
+
+
+
+Field
+
+ |
+
+
+
+Description
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+metadata
+
+Kubernetes meta/v1.ObjectMeta
+
+ |
+
+
+
+Refer to the Kubernetes API documentation for the fields of the
+metadata field.
+
+ |
+
+
+
+
+
+
+
+status
+ GatewayStatus
+
+ |
+
+
+
+ |
+
+
+
+
+
+
+
+spec
+GatewaySpec
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+template
+
+Kubernetes core/v1.PodTemplateSpec
+
+ |
+
+
+
+
+
+Template is the pod specification for the gateway Refer
+https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.11/\#pod-v1-core
+
+
+
+ |
+
+
+
+
+
+
+
+eventSourceRef
+ EventSourceRef
+
+
+ |
+
+
+
+
+
+EventSourceRef refers to event-source that stores event source
+configurations for the gateway
+
+
+
+ |
+
+
+
+
+
+
+
+type Argo Events common.EventSourceType
+
+ |
+
+
+
+
+
+Type is the type of gateway. Used as metadata.
+
+
+
+ |
+
+
+
+
+
+
+
+service
+
+Kubernetes core/v1.Service
+
+ |
+
+
+
+
+
+Service is the specifications of the service to expose the gateway Refer
+https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.11/\#service-v1-core
+
+
+
+ |
+
+
+
+
+
+
+
+watchers
+
+NotificationWatchers
+
+ |
+
+
+
+
+
+Watchers are components which are interested listening to notifications
+from this gateway These only need to be specified when gateway dispatch
+mechanism is through HTTP POST notifications. In future, support for
+NATS, KAFKA will be added as a means to dispatch notifications in which
+case specifying watchers would be unnecessary.
+
+
+
+ |
+
+
+
+
+
+
+
+processorPort string
+
+ |
+
+
+
+
+
+Port on which the gateway event source processor is running on.
+
+
+
+ |
+
+
+
+
+
+
+
+eventProtocol Argo Events common.EventProtocol
+
+
+ |
+
+
+
+
+
+EventProtocol is the underlying protocol used to send events from
+gateway to watchers(components interested in listening to event from
+this gateway)
+
+
+
+ |
+
+
+
+
+
+
+
+replica int
+
+ |
+
+
+
+
+
+Replica is the gateway deployment replicas
+
+
+
+ |
+
+
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+GatewayNotificationWatcher
+
+
+
+
+
+(Appears on:
+NotificationWatchers)
+
+
+
+
+
+
+
+GatewayNotificationWatcher is the gateway interested in listening to
+notifications from this gateway
+
+
+
+
+
+
+
+
+
+
+
+
+
+Field
+
+ |
+
+
+
+Description
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+name string
+
+ |
+
+
+
+
+
+Name is the gateway name
+
+
+
+ |
+
+
+
+
+
+
+
+port string
+
+ |
+
+
+
+
+
+Port is http server port on which gateway is running
+
+
+
+ |
+
+
+
+
+
+
+
+endpoint string
+
+ |
+
+
+
+
+
+Endpoint is REST API endpoint to post event to. Events are sent using
+HTTP POST method to this endpoint.
+
+
+
+ |
+
+
+
+
+
+
+
+namespace string
+
+ |
+
+
+
+
+
+Namespace of the gateway
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+GatewayResource
+
+
+
+
+
+(Appears on:
+GatewayStatus)
+
+
+
+
+
+
+
+GatewayResource holds the metadata about the gateway resources
+
+
+
+
+
+
+
+
+
+
+
+
+
+Field
+
+ |
+
+
+
+Description
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+deployment
+
+Kubernetes meta/v1.ObjectMeta
+
+ |
+
+
+
+
+
+Metadata of the deployment for the gateway
+
+
+
+ |
+
+
+
+
+
+
+
+service
+
+Kubernetes meta/v1.ObjectMeta
+
+ |
+
+
+
+(Optional)
+
+
+
+Metadata of the service for the gateway
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+GatewaySpec
+
+
+
+
+
+(Appears on:
+Gateway)
+
+
+
+
+
+
+
+GatewaySpec represents gateway specifications
+
+
+
+
+
+
+
+
+
+
+
+
+
+Field
+
+ |
+
+
+
+Description
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+template
+
+Kubernetes core/v1.PodTemplateSpec
+
+ |
+
+
+
+
+
+Template is the pod specification for the gateway Refer
+https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.11/\#pod-v1-core
+
+
+
+ |
+
+
+
+
+
+
+
+eventSourceRef
+ EventSourceRef
+
+
+ |
+
+
+
+
+
+EventSourceRef refers to event-source that stores event source
+configurations for the gateway
+
+
+
+ |
+
+
+
+
+
+
+
+type Argo Events common.EventSourceType
+
+ |
+
+
+
+
+
+Type is the type of gateway. Used as metadata.
+
+
+
+ |
+
+
+
+
+
+
+
+service
+
+Kubernetes core/v1.Service
+
+ |
+
+
+
+
+
+Service is the specifications of the service to expose the gateway Refer
+https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.11/\#service-v1-core
+
+
+
+ |
+
+
+
+
+
+
+
+watchers
+
+NotificationWatchers
+
+ |
+
+
+
+
+
+Watchers are components which are interested listening to notifications
+from this gateway These only need to be specified when gateway dispatch
+mechanism is through HTTP POST notifications. In future, support for
+NATS, KAFKA will be added as a means to dispatch notifications in which
+case specifying watchers would be unnecessary.
+
+
+
+ |
+
+
+
+
+
+
+
+processorPort string
+
+ |
+
+
+
+
+
+Port on which the gateway event source processor is running on.
+
+
+
+ |
+
+
+
+
+
+
+
+eventProtocol Argo Events common.EventProtocol
+
+
+ |
+
+
+
+
+
+EventProtocol is the underlying protocol used to send events from
+gateway to watchers(components interested in listening to event from
+this gateway)
+
+
+
+ |
+
+
+
+
+
+
+
+replica int
+
+ |
+
+
+
+
+
+Replica is the gateway deployment replicas
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+GatewayStatus
+
+
+
+
+
+(Appears on:
+Gateway)
+
+
+
+
+
+
+
+GatewayStatus contains information about the status of a gateway.
+
+
+
+
+
+
+
+
+
+
+
+
+
+Field
+
+ |
+
+
+
+Description
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+phase
+NodePhase
+
+ |
+
+
+
+
+
+Phase is the high-level summary of the gateway
+
+
+
+ |
+
+
+
+
+
+
+
+startedAt
+
+Kubernetes meta/v1.Time
+
+ |
+
+
+
+
+
+StartedAt is the time at which this gateway was initiated
+
+
+
+ |
+
+
+
+
+
+
+
+message string
+
+ |
+
+
+
+
+
+Message is a human readable string indicating details about a gateway in
+its phase
+
+
+
+ |
+
+
+
+
+
+
+
+nodes
+map\[string\]github.com/argoproj/argo-events/pkg/apis/gateway/v1alpha1.NodeStatus
+
+
+ |
+
+
+
+
+
+Nodes is a mapping between a node ID and the node’s status it records
+the states for the configurations of gateway.
+
+
+
+ |
+
+
+
+
+
+
+
+resources
+ GatewayResource
+
+
+ |
+
+
+
+
+
+Resources refers to the metadata about the gateway resources
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+NodePhase (string
alias)
+
+
+
+
+
+
+
+(Appears on:
+GatewayStatus,
+NodeStatus)
+
+
+
+
+
+
+
+NodePhase is the label for the condition of a node.
+
+
+
+
+
+
+
+NodeStatus
+
+
+
+
+
+(Appears on:
+GatewayStatus)
+
+
+
+
+
+
+
+NodeStatus describes the status for an individual node in the gateway
+configurations. A single node can represent one configuration.
+
+
+
+
+
+
+
+
+
+
+
+
+
+Field
+
+ |
+
+
+
+Description
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+id string
+
+ |
+
+
+
+
+
+ID is a unique identifier of a node within a sensor It is a hash of the
+node name
+
+
+
+ |
+
+
+
+
+
+
+
+name string
+
+ |
+
+
+
+
+
+Name is a unique name in the node tree used to generate the node ID
+
+
+
+ |
+
+
+
+
+
+
+
+displayName string
+
+ |
+
+
+
+
+
+DisplayName is the human readable representation of the node
+
+
+
+ |
+
+
+
+
+
+
+
+phase
+NodePhase
+
+ |
+
+
+
+
+
+Phase of the node
+
+
+
+ |
+
+
+
+
+
+
+
+startedAt
+
+Kubernetes meta/v1.MicroTime
+
+ |
+
+
+
+
+
+StartedAt is the time at which this node started
+
+
+
+ |
+
+
+
+
+
+
+
+message string
+
+ |
+
+
+
+
+
+Message store data or something to save for configuration
+
+
+
+ |
+
+
+
+
+
+
+
+updateTime
+
+Kubernetes meta/v1.MicroTime
+
+ |
+
+
+
+
+
+UpdateTime is the time when node(gateway configuration) was updated
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+NotificationWatchers
+
+
+
+
+
+(Appears on:
+GatewaySpec)
+
+
+
+
+
+
+
+NotificationWatchers are components which are interested listening to
+notifications from this gateway
+
+
+
+
+
+
+
+
+
+
+
+
+
+Field
+
+ |
+
+
+
+Description
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+gateways
+
+\[\]GatewayNotificationWatcher
+
+ |
+
+
+
+
+
+Gateways is the list of gateways interested in listening to
+notifications from this gateway
+
+
+
+ |
+
+
+
+
+
+
+
+sensors
+
+\[\]SensorNotificationWatcher
+
+ |
+
+
+
+
+
+Sensors is the list of sensors interested in listening to notifications
+from this gateway
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+SensorNotificationWatcher
+
+
+
+
+
+(Appears on:
+NotificationWatchers)
+
+
+
+
+
+
+
+SensorNotificationWatcher is the sensor interested in listening to
+notifications from this gateway
+
+
+
+
+
+
+
+
+
+
+
+
+
+Field
+
+ |
+
+
+
+Description
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+name string
+
+ |
+
+
+
+
+
+Name is the name of the sensor
+
+
+
+ |
+
+
+
+
+
+
+
+namespace string
+
+ |
+
+
+
+
+
+Namespace of the sensor
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+ Generated with gen-crd-api-reference-docs
on git
+commit 8d85191
.
+
+
diff --git a/api/generate.sh b/api/generate.sh
new file mode 100644
index 0000000000..5c50365bfd
--- /dev/null
+++ b/api/generate.sh
@@ -0,0 +1,5 @@
+#!/usr/bin/env sh
+
+pandoc --from markdown --to gfm event-source.html > event-source.md
+pandoc --from markdown --to gfm gateway.html > gateway.md
+pandoc --from markdown --to gfm sensor.html > sensor.md
diff --git a/api/sensor.html b/api/sensor.html
new file mode 100644
index 0000000000..d82d21b636
--- /dev/null
+++ b/api/sensor.html
@@ -0,0 +1,1816 @@
+Packages:
+
+argoproj.io/v1alpha1
+
+
Package v1alpha1 is the v1alpha1 version of the API.
+
+Resource Types:
+
+ArtifactLocation
+
+
+(Appears on:
+TriggerTemplate)
+
+
+
ArtifactLocation describes the source location for an external minio
+
+
+
+
+Field |
+Description |
+
+
+
+
+
+s3
+
+Argo Events common.S3Artifact
+
+ |
+
+ S3 compliant minio
+ |
+
+
+
+inline
+
+string
+
+ |
+
+ Inline minio is embedded in sensor spec as a string
+ |
+
+
+
+file
+
+
+FileArtifact
+
+
+ |
+
+ File minio is minio stored in a file
+ |
+
+
+
+url
+
+
+URLArtifact
+
+
+ |
+
+ URL to fetch the minio from
+ |
+
+
+
+configmap
+
+
+ConfigmapArtifact
+
+
+ |
+
+ Configmap that stores the minio
+ |
+
+
+
+git
+
+
+GitArtifact
+
+
+ |
+
+ Git repository hosting the minio
+ |
+
+
+
+resource
+
+
+Kubernetes meta/v1/unstructured.Unstructured
+
+
+ |
+
+ Resource is generic template for K8s resource
+ |
+
+
+
+Backoff
+
+
+(Appears on:
+TriggerPolicy)
+
+
+
Backoff for an operation
+
+
+
+
+Field |
+Description |
+
+
+
+
+
+duration
+
+time.Duration
+
+ |
+
+ Duration is the duration in nanoseconds
+ |
+
+
+
+factor
+
+float64
+
+ |
+
+ Duration is multiplied by factor each iteration
+ |
+
+
+
+jitter
+
+float64
+
+ |
+
+ The amount of jitter applied each iteration
+ |
+
+
+
+steps
+
+int
+
+ |
+
+ Exit with error after this many steps
+ |
+
+
+
+ConfigmapArtifact
+
+
+(Appears on:
+ArtifactLocation)
+
+
+
ConfigmapArtifact contains information about minio in k8 configmap
+
+
+
+
+Field |
+Description |
+
+
+
+
+
+name
+
+string
+
+ |
+
+ Name of the configmap
+ |
+
+
+
+namespace
+
+string
+
+ |
+
+ Namespace where configmap is deployed
+ |
+
+
+
+key
+
+string
+
+ |
+
+ Key within configmap data which contains trigger resource definition
+ |
+
+
+
+DataFilter
+
+
+(Appears on:
+EventDependencyFilter)
+
+
+
DataFilter describes constraints and filters for event data
+Regular Expressions are purposefully not a feature as they are overkill for our uses here
+See Rob Pike’s Post: https://commandcenter.blogspot.com/2011/08/regular-expressions-in-lexing-and.html
+
+
+
+
+Field |
+Description |
+
+
+
+
+
+path
+
+string
+
+ |
+
+ Path is the JSONPath of the event’s (JSON decoded) data key
+Path is a series of keys separated by a dot. A key may contain wildcard characters ‘*’ and ‘?’.
+To access an array value use the index as the key. The dot and wildcard characters can be escaped with ‘\’.
+See https://github.com/tidwall/gjson#path-syntax for more information on how to use this.
+ |
+
+
+
+type
+
+
+JSONType
+
+
+ |
+
+ Type contains the JSON type of the data
+ |
+
+
+
+value
+
+[]string
+
+ |
+
+ Value is the allowed string values for this key
+Booleans are passed using strconv.ParseBool()
+Numbers are parsed using as float64 using strconv.ParseFloat()
+Strings are taken as is
+Nils this value is ignored
+ |
+
+
+
+DependencyGroup
+
+
+(Appears on:
+SensorSpec)
+
+
+
DependencyGroup is the group of dependencies
+
+
+
+
+Field |
+Description |
+
+
+
+
+
+name
+
+string
+
+ |
+
+ Name of the group
+ |
+
+
+
+dependencies
+
+[]string
+
+ |
+
+ Dependencies of events
+ |
+
+
+
+EventDependency
+
+
+(Appears on:
+SensorSpec)
+
+
+
EventDependency describes a dependency
+
+
+
+
+Field |
+Description |
+
+
+
+
+
+name
+
+string
+
+ |
+
+ Name is a unique name of this dependency
+ |
+
+
+
+filters
+
+
+EventDependencyFilter
+
+
+ |
+
+ Filters and rules governing tolerations of success and constraints on the context and data of an event
+ |
+
+
+
+connected
+
+bool
+
+ |
+
+ Connected tells if subscription is already setup in case of nats protocol.
+ |
+
+
+
+EventDependencyFilter
+
+
+(Appears on:
+EventDependency)
+
+
+
EventDependencyFilter defines filters and constraints for a event.
+
+
+
+
+Field |
+Description |
+
+
+
+
+
+name
+
+string
+
+ |
+
+ Name is the name of event filter
+ |
+
+
+
+time
+
+
+TimeFilter
+
+
+ |
+
+ Time filter on the event with escalation
+ |
+
+
+
+context
+
+Argo Events common.EventContext
+
+ |
+
+ Context filter constraints with escalation
+ |
+
+
+
+data
+
+
+[]DataFilter
+
+
+ |
+
+ Data filter constraints with escalation
+ |
+
+
+
+FileArtifact
+
+
+(Appears on:
+ArtifactLocation)
+
+
+
FileArtifact contains information about an minio in a filesystem
+
+
+
+
+Field |
+Description |
+
+
+
+
+
+path
+
+string
+
+ |
+
+ |
+
+
+
+GitArtifact
+
+
+(Appears on:
+ArtifactLocation)
+
+
+
GitArtifact contains information about an minio stored in git
+
+
+
+
+Field |
+Description |
+
+
+
+
+
+url
+
+string
+
+ |
+
+ Git URL
+ |
+
+
+
+cloneDirectory
+
+string
+
+ |
+
+ Directory to clone the repository. We clone complete directory because GitArtifact is not limited to any specific Git service providers.
+Hence we don’t use any specific git provider client.
+ |
+
+
+
+creds
+
+
+GitCreds
+
+
+ |
+
+(Optional)
+ Creds contain reference to git username and password
+ |
+
+
+
+namespace
+
+string
+
+ |
+
+(Optional)
+ Namespace where creds are stored.
+ |
+
+
+
+sshKeyPath
+
+string
+
+ |
+
+(Optional)
+ SSHKeyPath is path to your ssh key path. Use this if you don’t want to provide username and password.
+ssh key path must be mounted in sensor pod.
+ |
+
+
+
+filePath
+
+string
+
+ |
+
+ Path to file that contains trigger resource definition
+ |
+
+
+
+branch
+
+string
+
+ |
+
+(Optional)
+ Branch to use to pull trigger resource
+ |
+
+
+
+tag
+
+string
+
+ |
+
+(Optional)
+ Tag to use to pull trigger resource
+ |
+
+
+
+ref
+
+string
+
+ |
+
+(Optional)
+ Ref to use to pull trigger resource. Will result in a shallow clone and
+fetch.
+ |
+
+
+
+remote
+
+
+GitRemoteConfig
+
+
+ |
+
+(Optional)
+ Remote to manage set of tracked repositories. Defaults to “origin”.
+Refer https://git-scm.com/docs/git-remote
+ |
+
+
+
+GitCreds
+
+
+(Appears on:
+GitArtifact)
+
+
+
GitCreds contain reference to git username and password
+
+
+GitRemoteConfig
+
+
+(Appears on:
+GitArtifact)
+
+
+
GitRemoteConfig contains the configuration of a Git remote
+
+
+
+
+Field |
+Description |
+
+
+
+
+
+name
+
+string
+
+ |
+
+ Name of the remote to fetch from.
+ |
+
+
+
+urls
+
+[]string
+
+ |
+
+ URLs the URLs of a remote repository. It must be non-empty. Fetch will
+always use the first URL, while push will use all of them.
+ |
+
+
+
+JSONType
+(string
alias)
+
+(Appears on:
+DataFilter)
+
+
+
JSONType contains the supported JSON types for data filtering
+
+NodePhase
+(string
alias)
+
+(Appears on:
+NodeStatus,
+SensorStatus)
+
+
+
NodePhase is the label for the condition of a node
+
+NodeStatus
+
+
+(Appears on:
+SensorStatus)
+
+
+
NodeStatus describes the status for an individual node in the sensor’s FSM.
+A single node can represent the status for event or a trigger.
+
+
+
+
+Field |
+Description |
+
+
+
+
+
+id
+
+string
+
+ |
+
+ ID is a unique identifier of a node within a sensor
+It is a hash of the node name
+ |
+
+
+
+name
+
+string
+
+ |
+
+ Name is a unique name in the node tree used to generate the node ID
+ |
+
+
+
+displayName
+
+string
+
+ |
+
+ DisplayName is the human readable representation of the node
+ |
+
+
+
+type
+
+
+NodeType
+
+
+ |
+
+ Type is the type of the node
+ |
+
+
+
+phase
+
+
+NodePhase
+
+
+ |
+
+ Phase of the node
+ |
+
+
+
+startedAt
+
+
+Kubernetes meta/v1.MicroTime
+
+
+ |
+
+ StartedAt is the time at which this node started
+ |
+
+
+
+completedAt
+
+
+Kubernetes meta/v1.MicroTime
+
+
+ |
+
+ CompletedAt is the time at which this node completed
+ |
+
+
+
+message
+
+string
+
+ |
+
+ store data or something to save for event notifications or trigger events
+ |
+
+
+
+event
+
+Argo Events common.Event
+
+ |
+
+ Event stores the last seen event for this node
+ |
+
+
+
+NodeType
+(string
alias)
+
+(Appears on:
+NodeStatus)
+
+
+
NodeType is the type of a node
+
+NotificationType
+(string
alias)
+
+
NotificationType represent a type of notifications that are handled by a sensor
+
+Sensor
+
+
+
Sensor is the definition of a sensor resource
+
+
+
+
+Field |
+Description |
+
+
+
+
+
+metadata
+
+
+Kubernetes meta/v1.ObjectMeta
+
+
+ |
+
+Refer to the Kubernetes API documentation for the fields of the
+metadata field.
+ |
+
+
+
+spec
+
+
+SensorSpec
+
+
+ |
+
+
+
+
+
+
+dependencies
+
+
+[]EventDependency
+
+
+ |
+
+ Dependencies is a list of the events that this sensor is dependent on.
+ |
+
+
+
+triggers
+
+
+[]Trigger
+
+
+ |
+
+ Triggers is a list of the things that this sensor evokes. These are the outputs from this sensor.
+ |
+
+
+
+template
+
+
+Kubernetes core/v1.PodTemplateSpec
+
+
+ |
+
+ Template contains sensor pod specification. For more information, read https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.11/#pod-v1-core
+ |
+
+
+
+eventProtocol
+
+Argo Events common.EventProtocol
+
+ |
+
+ EventProtocol is the protocol through which sensor receives events from gateway
+ |
+
+
+
+circuit
+
+string
+
+ |
+
+ Circuit is a boolean expression of dependency groups
+ |
+
+
+
+dependencyGroups
+
+
+[]DependencyGroup
+
+
+ |
+
+ DependencyGroups is a list of the groups of events.
+ |
+
+
+
+errorOnFailedRound
+
+bool
+
+ |
+
+ ErrorOnFailedRound if set to true, marks sensor state as error if the previous trigger round fails.
+Once sensor state is set to error , no further triggers will be processed.
+ |
+
+
+ |
+
+
+
+status
+
+
+SensorStatus
+
+
+ |
+
+ |
+
+
+
+SensorResources
+
+
+(Appears on:
+SensorStatus)
+
+
+
SensorResources holds the metadata of the resources created for the sensor
+
+
+SensorSpec
+
+
+(Appears on:
+Sensor)
+
+
+
SensorSpec represents desired sensor state
+
+
+
+
+Field |
+Description |
+
+
+
+
+
+dependencies
+
+
+[]EventDependency
+
+
+ |
+
+ Dependencies is a list of the events that this sensor is dependent on.
+ |
+
+
+
+triggers
+
+
+[]Trigger
+
+
+ |
+
+ Triggers is a list of the things that this sensor evokes. These are the outputs from this sensor.
+ |
+
+
+
+template
+
+
+Kubernetes core/v1.PodTemplateSpec
+
+
+ |
+
+ Template contains sensor pod specification. For more information, read https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.11/#pod-v1-core
+ |
+
+
+
+eventProtocol
+
+Argo Events common.EventProtocol
+
+ |
+
+ EventProtocol is the protocol through which sensor receives events from gateway
+ |
+
+
+
+circuit
+
+string
+
+ |
+
+ Circuit is a boolean expression of dependency groups
+ |
+
+
+
+dependencyGroups
+
+
+[]DependencyGroup
+
+
+ |
+
+ DependencyGroups is a list of the groups of events.
+ |
+
+
+
+errorOnFailedRound
+
+bool
+
+ |
+
+ ErrorOnFailedRound if set to true, marks sensor state as error if the previous trigger round fails.
+Once sensor state is set to error , no further triggers will be processed.
+ |
+
+
+
+SensorStatus
+
+
+(Appears on:
+Sensor)
+
+
+
SensorStatus contains information about the status of a sensor.
+
+
+
+
+Field |
+Description |
+
+
+
+
+
+phase
+
+
+NodePhase
+
+
+ |
+
+ Phase is the high-level summary of the sensor
+ |
+
+
+
+startedAt
+
+
+Kubernetes meta/v1.Time
+
+
+ |
+
+ StartedAt is the time at which this sensor was initiated
+ |
+
+
+
+completedAt
+
+
+Kubernetes meta/v1.Time
+
+
+ |
+
+ CompletedAt is the time at which this sensor was completed
+ |
+
+
+
+message
+
+string
+
+ |
+
+ Message is a human readable string indicating details about a sensor in its phase
+ |
+
+
+
+nodes
+
+
+map[string]github.com/argoproj/argo-events/pkg/apis/sensor/v1alpha1.NodeStatus
+
+
+ |
+
+ Nodes is a mapping between a node ID and the node’s status
+it records the states for the FSM of this sensor.
+ |
+
+
+
+triggerCycleCount
+
+int32
+
+ |
+
+ TriggerCycleCount is the count of sensor’s trigger cycle runs.
+ |
+
+
+
+triggerCycleStatus
+
+
+TriggerCycleState
+
+
+ |
+
+ TriggerCycleState is the status from last cycle of triggers execution.
+ |
+
+
+
+lastCycleTime
+
+
+Kubernetes meta/v1.Time
+
+
+ |
+
+ LastCycleTime is the time when last trigger cycle completed
+ |
+
+
+
+resources
+
+
+SensorResources
+
+
+ |
+
+ Resources refers to metadata of the resources created for the sensor
+ |
+
+
+
+TimeFilter
+
+
+(Appears on:
+EventDependencyFilter)
+
+
+
TimeFilter describes a window in time.
+DataFilters out event events that occur outside the time limits.
+In other words, only events that occur after Start and before Stop
+will pass this filter.
+
+
+
+
+Field |
+Description |
+
+
+
+
+
+start
+
+string
+
+ |
+
+ Start is the beginning of a time window.
+Before this time, events for this event are ignored and
+format is hh:mm:ss
+ |
+
+
+
+stop
+
+string
+
+ |
+
+ StopPattern is the end of a time window.
+After this time, events for this event are ignored and
+format is hh:mm:ss
+ |
+
+
+
+Trigger
+
+
+(Appears on:
+SensorSpec)
+
+
+
Trigger is an action taken, output produced, an event created, a message sent
+
+
+
+
+Field |
+Description |
+
+
+
+
+
+template
+
+
+TriggerTemplate
+
+
+ |
+
+ Template describes the trigger specification.
+ |
+
+
+
+templateParameters
+
+
+[]TriggerParameter
+
+
+ |
+
+ TemplateParameters is the list of resource parameters to pass to the template object
+ |
+
+
+
+resourceParameters
+
+
+[]TriggerParameter
+
+
+ |
+
+ ResourceParameters is the list of resource parameters to pass to resolved resource object in template object
+ |
+
+
+
+policy
+
+
+TriggerPolicy
+
+
+ |
+
+ Policy to configure backoff and execution criteria for the trigger
+ |
+
+
+
+TriggerCondition
+
+
+(Appears on:
+TriggerTemplate)
+
+
+
TriggerCondition describes condition which must be satisfied in order to execute a trigger.
+Depending upon condition type, status of dependency groups is used to evaluate the result.
+
+
+
+
+Field |
+Description |
+
+
+
+
+
+any
+
+[]string
+
+ |
+
+ Any acts as a OR operator between dependencies
+ |
+
+
+
+all
+
+[]string
+
+ |
+
+ All acts as a AND operator between dependencies
+ |
+
+
+
+TriggerCycleState
+(string
alias)
+
+(Appears on:
+SensorStatus)
+
+
+
TriggerCycleState is the label for the state of the trigger cycle
+
+TriggerParameter
+
+
+(Appears on:
+Trigger)
+
+
+
TriggerParameter indicates a passed parameter to a service template
+
+
+
+
+Field |
+Description |
+
+
+
+
+
+src
+
+
+TriggerParameterSource
+
+
+ |
+
+ Src contains a source reference to the value of the parameter from a event event
+ |
+
+
+
+dest
+
+string
+
+ |
+
+ Dest is the JSONPath of a resource key.
+A path is a series of keys separated by a dot. The colon character can be escaped with ‘.’
+The -1 key can be used to append a value to an existing array.
+See https://github.com/tidwall/sjson#path-syntax for more information about how this is used.
+ |
+
+
+
+operation
+
+
+TriggerParameterOperation
+
+
+ |
+
+ Operation is what to do with the existing value at Dest, whether to
+‘prepend’, ‘overwrite’, or ‘append’ it.
+ |
+
+
+
+TriggerParameterOperation
+(string
alias)
+
+(Appears on:
+TriggerParameter)
+
+
+
TriggerParameterOperation represents how to set a trigger destination
+resource key
+
+TriggerParameterSource
+
+
+(Appears on:
+TriggerParameter)
+
+
+
TriggerParameterSource defines the source for a parameter from a event event
+
+
+
+
+Field |
+Description |
+
+
+
+
+
+event
+
+string
+
+ |
+
+ Event is the name of the event for which to retrieve this event
+ |
+
+
+
+path
+
+string
+
+ |
+
+ Path is the JSONPath of the event’s (JSON decoded) data key
+Path is a series of keys separated by a dot. A key may contain wildcard characters ‘*’ and ‘?’.
+To access an array value use the index as the key. The dot and wildcard characters can be escaped with ‘\’.
+See https://github.com/tidwall/gjson#path-syntax for more information on how to use this.
+ |
+
+
+
+value
+
+string
+
+ |
+
+ Value is the default literal value to use for this parameter source
+This is only used if the path is invalid.
+If the path is invalid and this is not defined, this param source will produce an error.
+ |
+
+
+
+TriggerPolicy
+
+
+(Appears on:
+Trigger)
+
+
+
TriggerPolicy dictates the policy for the trigger retries
+
+
+
+
+Field |
+Description |
+
+
+
+
+
+backoff
+
+
+Backoff
+
+
+ |
+
+ Backoff before checking resource state
+ |
+
+
+
+state
+
+
+TriggerStateLabels
+
+
+ |
+
+ State refers to labels used to check the resource state
+ |
+
+
+
+errorOnBackoffTimeout
+
+bool
+
+ |
+
+ ErrorOnBackoffTimeout determines whether sensor should transition to error state if the backoff times out and yet the resource neither transitioned into success or failure.
+ |
+
+
+
+TriggerStateLabels
+
+
+(Appears on:
+TriggerPolicy)
+
+
+
TriggerStateLabels defines the labels used to decide if a resource is in success or failure state.
+
+
+
+
+Field |
+Description |
+
+
+
+
+
+success
+
+map[string]string
+
+ |
+
+ Success defines labels required to identify a resource in success state
+ |
+
+
+
+failure
+
+map[string]string
+
+ |
+
+ Failure defines labels required to identify a resource in failed state
+ |
+
+
+
+TriggerTemplate
+
+
+(Appears on:
+Trigger)
+
+
+
TriggerTemplate is the template that describes trigger specification.
+
+
+
+
+Field |
+Description |
+
+
+
+
+
+name
+
+string
+
+ |
+
+ Name is a unique name of the action to take
+ |
+
+
+
+when
+
+
+TriggerCondition
+
+
+ |
+
+ When is the condition to execute the trigger
+ |
+
+
+
+GroupVersionResource
+
+
+Kubernetes meta/v1.GroupVersionResource
+
+
+ |
+
+
+(Members of GroupVersionResource are embedded into this type.)
+
+The unambiguous kind of this object - used in order to retrieve the appropriate kubernetes api client for this resource
+ |
+
+
+
+source
+
+
+ArtifactLocation
+
+
+ |
+
+ Source of the K8 resource file(s)
+ |
+
+
+
+URLArtifact
+
+
+(Appears on:
+ArtifactLocation)
+
+
+
URLArtifact contains information about an minio at an http endpoint.
+
+
+
+
+Field |
+Description |
+
+
+
+
+
+path
+
+string
+
+ |
+
+ Path is the complete URL
+ |
+
+
+
+verifyCert
+
+bool
+
+ |
+
+ VerifyCert decides whether the connection is secure or not
+ |
+
+
+
+
+
+Generated with gen-crd-api-reference-docs
+on git commit 8d85191
.
+
diff --git a/api/sensor.md b/api/sensor.md
new file mode 100644
index 0000000000..bef98f80db
--- /dev/null
+++ b/api/sensor.md
@@ -0,0 +1,3661 @@
+
+
+Packages:
+
+
+
+
+
+
+
+argoproj.io/v1alpha1
+
+
+
+
+
+
+
+Package v1alpha1 is the v1alpha1 version of the API.
+
+
+
+
+
+Resource Types:
+
+
+
+
+
+ArtifactLocation
+
+
+
+
+
+(Appears on:
+TriggerTemplate)
+
+
+
+
+
+
+
+ArtifactLocation describes the source location for an external minio
+
+
+
+
+
+
+
+
+
+
+
+
+
+Field
+
+ |
+
+
+
+Description
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+s3 Argo Events common.S3Artifact
+
+ |
+
+
+
+
+
+S3 compliant minio
+
+
+
+ |
+
+
+
+
+
+
+
+inline string
+
+ |
+
+
+
+
+
+Inline minio is embedded in sensor spec as a string
+
+
+
+ |
+
+
+
+
+
+
+
+file
+ FileArtifact
+
+ |
+
+
+
+
+
+File minio is minio stored in a file
+
+
+
+ |
+
+
+
+
+
+
+
+url
+URLArtifact
+
+ |
+
+
+
+
+
+URL to fetch the minio from
+
+
+
+ |
+
+
+
+
+
+
+
+configmap
+ ConfigmapArtifact
+
+
+ |
+
+
+
+
+
+Configmap that stores the minio
+
+
+
+ |
+
+
+
+
+
+
+
+git
+GitArtifact
+
+ |
+
+
+
+
+
+Git repository hosting the minio
+
+
+
+ |
+
+
+
+
+
+
+
+resource
+
+Kubernetes meta/v1/unstructured.Unstructured
+
+ |
+
+
+
+
+
+Resource is generic template for K8s resource
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+Backoff
+
+
+
+
+
+(Appears on:
+TriggerPolicy)
+
+
+
+
+
+
+
+Backoff for an operation
+
+
+
+
+
+
+
+
+
+
+
+
+
+Field
+
+ |
+
+
+
+Description
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+duration time.Duration
+
+ |
+
+
+
+
+
+Duration is the duration in nanoseconds
+
+
+
+ |
+
+
+
+
+
+
+
+factor float64
+
+ |
+
+
+
+
+
+Duration is multiplied by factor each iteration
+
+
+
+ |
+
+
+
+
+
+
+
+jitter float64
+
+ |
+
+
+
+
+
+The amount of jitter applied each iteration
+
+
+
+ |
+
+
+
+
+
+
+
+steps int
+
+ |
+
+
+
+
+
+Exit with error after this many steps
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+ConfigmapArtifact
+
+
+
+
+
+(Appears on:
+ArtifactLocation)
+
+
+
+
+
+
+
+ConfigmapArtifact contains information about minio in k8 configmap
+
+
+
+
+
+
+
+
+
+
+
+
+
+Field
+
+ |
+
+
+
+Description
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+name string
+
+ |
+
+
+
+
+
+Name of the configmap
+
+
+
+ |
+
+
+
+
+
+
+
+namespace string
+
+ |
+
+
+
+
+
+Namespace where configmap is deployed
+
+
+
+ |
+
+
+
+
+
+
+
+key string
+
+ |
+
+
+
+
+
+Key within configmap data which contains trigger resource definition
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+DataFilter
+
+
+
+
+
+(Appears on:
+EventDependencyFilter)
+
+
+
+
+
+
+
+DataFilter describes constraints and filters for event data Regular
+Expressions are purposefully not a feature as they are overkill for our
+uses here See Rob Pike’s Post:
+https://commandcenter.blogspot.com/2011/08/regular-expressions-in-lexing-and.html
+
+
+
+
+
+
+
+
+
+
+
+
+
+Field
+
+ |
+
+
+
+Description
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+path string
+
+ |
+
+
+
+
+
+Path is the JSONPath of the event’s (JSON decoded) data key Path is a
+series of keys separated by a dot. A key may contain wildcard characters
+‘\*’ and ‘?’. To access an array value use the index as the key. The dot
+and wildcard characters can be escaped with ‘\’. See
+https://github.com/tidwall/gjson\#path-syntax
+for more information on how to use this.
+
+
+
+ |
+
+
+
+
+
+
+
+type
+JSONType
+
+ |
+
+
+
+
+
+Type contains the JSON type of the data
+
+
+
+ |
+
+
+
+
+
+
+
+value \[\]string
+
+ |
+
+
+
+
+
+Value is the allowed string values for this key Booleans are passed
+using strconv.ParseBool() Numbers are parsed using as float64 using
+strconv.ParseFloat() Strings are taken as is Nils this value is ignored
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+DependencyGroup
+
+
+
+
+
+(Appears on:
+SensorSpec)
+
+
+
+
+
+
+
+DependencyGroup is the group of dependencies
+
+
+
+
+
+
+
+
+
+
+
+
+
+Field
+
+ |
+
+
+
+Description
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+name string
+
+ |
+
+
+
+
+
+Name of the group
+
+
+
+ |
+
+
+
+
+
+
+
+dependencies \[\]string
+
+ |
+
+
+
+
+
+Dependencies of events
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+EventDependency
+
+
+
+
+
+(Appears on:
+SensorSpec)
+
+
+
+
+
+
+
+EventDependency describes a dependency
+
+
+
+
+
+
+
+
+
+
+
+
+
+Field
+
+ |
+
+
+
+Description
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+name string
+
+ |
+
+
+
+
+
+Name is a unique name of this dependency
+
+
+
+ |
+
+
+
+
+
+
+
+filters
+
+EventDependencyFilter
+
+ |
+
+
+
+
+
+Filters and rules governing tolerations of success and constraints on
+the context and data of an event
+
+
+
+ |
+
+
+
+
+
+
+
+connected bool
+
+ |
+
+
+
+
+
+Connected tells if subscription is already setup in case of nats
+protocol.
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+EventDependencyFilter
+
+
+
+
+
+(Appears on:
+EventDependency)
+
+
+
+
+
+
+
+EventDependencyFilter defines filters and constraints for a event.
+
+
+
+
+
+
+
+
+
+
+
+
+
+Field
+
+ |
+
+
+
+Description
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+name string
+
+ |
+
+
+
+
+
+Name is the name of event filter
+
+
+
+ |
+
+
+
+
+
+
+
+time
+TimeFilter
+
+ |
+
+
+
+
+
+Time filter on the event with escalation
+
+
+
+ |
+
+
+
+
+
+
+
+context Argo Events common.EventContext
+
+ |
+
+
+
+
+
+Context filter constraints with escalation
+
+
+
+ |
+
+
+
+
+
+
+
+data
+\[\]DataFilter
+
+ |
+
+
+
+
+
+Data filter constraints with escalation
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+FileArtifact
+
+
+
+
+
+(Appears on:
+ArtifactLocation)
+
+
+
+
+
+
+
+FileArtifact contains information about an minio in a filesystem
+
+
+
+
+
+
+
+
+
+
+
+
+
+Field
+
+ |
+
+
+
+Description
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+path string
+
+ |
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+GitArtifact
+
+
+
+
+
+(Appears on:
+ArtifactLocation)
+
+
+
+
+
+
+
+GitArtifact contains information about an minio stored in git
+
+
+
+
+
+
+
+
+
+
+
+
+
+Field
+
+ |
+
+
+
+Description
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+url string
+
+ |
+
+
+
+
+
+Git URL
+
+
+
+ |
+
+
+
+
+
+
+
+cloneDirectory string
+
+ |
+
+
+
+
+
+Directory to clone the repository. We clone complete directory because
+GitArtifact is not limited to any specific Git service providers. Hence
+we don’t use any specific git provider client.
+
+
+
+ |
+
+
+
+
+
+
+
+creds
+GitCreds
+
+ |
+
+
+
+(Optional)
+
+
+
+Creds contain reference to git username and password
+
+
+
+ |
+
+
+
+
+
+
+
+namespace string
+
+ |
+
+
+
+(Optional)
+
+
+
+Namespace where creds are stored.
+
+
+
+ |
+
+
+
+
+
+
+
+sshKeyPath string
+
+ |
+
+
+
+(Optional)
+
+
+
+SSHKeyPath is path to your ssh key path. Use this if you don’t want to
+provide username and password. ssh key path must be mounted in sensor
+pod.
+
+
+
+ |
+
+
+
+
+
+
+
+filePath string
+
+ |
+
+
+
+
+
+Path to file that contains trigger resource definition
+
+
+
+ |
+
+
+
+
+
+
+
+branch string
+
+ |
+
+
+
+(Optional)
+
+
+
+Branch to use to pull trigger resource
+
+
+
+ |
+
+
+
+
+
+
+
+tag string
+
+ |
+
+
+
+(Optional)
+
+
+
+Tag to use to pull trigger resource
+
+
+
+ |
+
+
+
+
+
+
+
+ref string
+
+ |
+
+
+
+(Optional)
+
+
+
+Ref to use to pull trigger resource. Will result in a shallow clone and
+fetch.
+
+
+
+ |
+
+
+
+
+
+
+
+remote
+ GitRemoteConfig
+
+
+ |
+
+
+
+(Optional)
+
+
+
+Remote to manage set of tracked repositories. Defaults to “origin”.
+Refer
+https://git-scm.com/docs/git-remote
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+GitCreds
+
+
+
+
+
+(Appears on:
+GitArtifact)
+
+
+
+
+
+
+
+GitCreds contain reference to git username and password
+
+
+
+
+
+
+
+
+
+GitRemoteConfig
+
+
+
+
+
+(Appears on:
+GitArtifact)
+
+
+
+
+
+
+
+GitRemoteConfig contains the configuration of a Git remote
+
+
+
+
+
+
+
+
+
+
+
+
+
+Field
+
+ |
+
+
+
+Description
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+name string
+
+ |
+
+
+
+
+
+Name of the remote to fetch from.
+
+
+
+ |
+
+
+
+
+
+
+
+urls \[\]string
+
+ |
+
+
+
+
+
+URLs the URLs of a remote repository. It must be non-empty. Fetch will
+always use the first URL, while push will use all of them.
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+JSONType (string
alias)
+
+
+
+
+
+
+
+(Appears on:
+DataFilter)
+
+
+
+
+
+
+
+JSONType contains the supported JSON types for data filtering
+
+
+
+
+
+
+
+NodePhase (string
alias)
+
+
+
+
+
+
+
+(Appears on:
+NodeStatus,
+SensorStatus)
+
+
+
+
+
+
+
+NodePhase is the label for the condition of a node
+
+
+
+
+
+
+
+NodeStatus
+
+
+
+
+
+(Appears on:
+SensorStatus)
+
+
+
+
+
+
+
+NodeStatus describes the status for an individual node in the sensor’s
+FSM. A single node can represent the status for event or a trigger.
+
+
+
+
+
+
+
+
+
+
+
+
+
+Field
+
+ |
+
+
+
+Description
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+id string
+
+ |
+
+
+
+
+
+ID is a unique identifier of a node within a sensor It is a hash of the
+node name
+
+
+
+ |
+
+
+
+
+
+
+
+name string
+
+ |
+
+
+
+
+
+Name is a unique name in the node tree used to generate the node ID
+
+
+
+ |
+
+
+
+
+
+
+
+displayName string
+
+ |
+
+
+
+
+
+DisplayName is the human readable representation of the node
+
+
+
+ |
+
+
+
+
+
+
+
+type
+NodeType
+
+ |
+
+
+
+
+
+Type is the type of the node
+
+
+
+ |
+
+
+
+
+
+
+
+phase
+NodePhase
+
+ |
+
+
+
+
+
+Phase of the node
+
+
+
+ |
+
+
+
+
+
+
+
+startedAt
+
+Kubernetes meta/v1.MicroTime
+
+ |
+
+
+
+
+
+StartedAt is the time at which this node started
+
+
+
+ |
+
+
+
+
+
+
+
+completedAt
+
+Kubernetes meta/v1.MicroTime
+
+ |
+
+
+
+
+
+CompletedAt is the time at which this node completed
+
+
+
+ |
+
+
+
+
+
+
+
+message string
+
+ |
+
+
+
+
+
+store data or something to save for event notifications or trigger
+events
+
+
+
+ |
+
+
+
+
+
+
+
+event Argo Events common.Event
+
+ |
+
+
+
+
+
+Event stores the last seen event for this node
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+NodeType (string
alias)
+
+
+
+
+
+
+
+(Appears on:
+NodeStatus)
+
+
+
+
+
+
+
+NodeType is the type of a node
+
+
+
+
+
+
+
+NotificationType (string
alias)
+
+
+
+
+
+
+
+
+
+NotificationType represent a type of notifications that are handled by a
+sensor
+
+
+
+
+
+
+
+Sensor
+
+
+
+
+
+
+
+Sensor is the definition of a sensor resource
+
+
+
+
+
+
+
+
+
+
+
+
+
+Field
+
+ |
+
+
+
+Description
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+metadata
+
+Kubernetes meta/v1.ObjectMeta
+
+ |
+
+
+
+Refer to the Kubernetes API documentation for the fields of the
+metadata field.
+
+ |
+
+
+
+
+
+
+
+spec
+SensorSpec
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+dependencies
+ \[\]EventDependency
+
+
+ |
+
+
+
+
+
+Dependencies is a list of the events that this sensor is dependent on.
+
+
+
+ |
+
+
+
+
+
+
+
+triggers
+\[\]Trigger
+
+ |
+
+
+
+
+
+Triggers is a list of the things that this sensor evokes. These are the
+outputs from this sensor.
+
+
+
+ |
+
+
+
+
+
+
+
+template
+
+Kubernetes core/v1.PodTemplateSpec
+
+ |
+
+
+
+
+
+Template contains sensor pod specification. For more information, read
+https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.11/\#pod-v1-core
+
+
+
+ |
+
+
+
+
+
+
+
+eventProtocol Argo Events common.EventProtocol
+
+
+ |
+
+
+
+
+
+EventProtocol is the protocol through which sensor receives events from
+gateway
+
+
+
+ |
+
+
+
+
+
+
+
+circuit string
+
+ |
+
+
+
+
+
+Circuit is a boolean expression of dependency groups
+
+
+
+ |
+
+
+
+
+
+
+
+dependencyGroups
+ \[\]DependencyGroup
+
+
+ |
+
+
+
+
+
+DependencyGroups is a list of the groups of events.
+
+
+
+ |
+
+
+
+
+
+
+
+errorOnFailedRound bool
+
+ |
+
+
+
+
+
+ErrorOnFailedRound if set to true, marks sensor state as
+error if the previous trigger round fails. Once sensor
+state is set to error , no further triggers will be
+processed.
+
+
+
+ |
+
+
+
+
+
+ |
+
+
+
+
+
+
+
+status
+ SensorStatus
+
+ |
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+SensorResources
+
+
+
+
+
+(Appears on:
+SensorStatus)
+
+
+
+
+
+
+
+SensorResources holds the metadata of the resources created for the
+sensor
+
+
+
+
+
+
+
+
+
+
+
+
+
+Field
+
+ |
+
+
+
+Description
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+deployment
+
+Kubernetes meta/v1.ObjectMeta
+
+ |
+
+
+
+
+
+Deployment holds the metadata of the deployment for the sensor
+
+
+
+ |
+
+
+
+
+
+
+
+service
+
+Kubernetes meta/v1.ObjectMeta
+
+ |
+
+
+
+(Optional)
+
+
+
+Service holds the metadata of the service for the sensor
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+SensorSpec
+
+
+
+
+
+(Appears on: Sensor)
+
+
+
+
+
+
+
+SensorSpec represents desired sensor state
+
+
+
+
+
+
+
+
+
+
+
+
+
+Field
+
+ |
+
+
+
+Description
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+dependencies
+ \[\]EventDependency
+
+
+ |
+
+
+
+
+
+Dependencies is a list of the events that this sensor is dependent on.
+
+
+
+ |
+
+
+
+
+
+
+
+triggers
+\[\]Trigger
+
+ |
+
+
+
+
+
+Triggers is a list of the things that this sensor evokes. These are the
+outputs from this sensor.
+
+
+
+ |
+
+
+
+
+
+
+
+template
+
+Kubernetes core/v1.PodTemplateSpec
+
+ |
+
+
+
+
+
+Template contains sensor pod specification. For more information, read
+https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.11/\#pod-v1-core
+
+
+
+ |
+
+
+
+
+
+
+
+eventProtocol Argo Events common.EventProtocol
+
+
+ |
+
+
+
+
+
+EventProtocol is the protocol through which sensor receives events from
+gateway
+
+
+
+ |
+
+
+
+
+
+
+
+circuit string
+
+ |
+
+
+
+
+
+Circuit is a boolean expression of dependency groups
+
+
+
+ |
+
+
+
+
+
+
+
+dependencyGroups
+ \[\]DependencyGroup
+
+
+ |
+
+
+
+
+
+DependencyGroups is a list of the groups of events.
+
+
+
+ |
+
+
+
+
+
+
+
+errorOnFailedRound bool
+
+ |
+
+
+
+
+
+ErrorOnFailedRound if set to true, marks sensor state as
+error if the previous trigger round fails. Once sensor
+state is set to error , no further triggers will be
+processed.
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+SensorStatus
+
+
+
+
+
+(Appears on: Sensor)
+
+
+
+
+
+
+
+SensorStatus contains information about the status of a sensor.
+
+
+
+
+
+
+
+
+
+
+
+
+
+Field
+
+ |
+
+
+
+Description
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+phase
+NodePhase
+
+ |
+
+
+
+
+
+Phase is the high-level summary of the sensor
+
+
+
+ |
+
+
+
+
+
+
+
+startedAt
+
+Kubernetes meta/v1.Time
+
+ |
+
+
+
+
+
+StartedAt is the time at which this sensor was initiated
+
+
+
+ |
+
+
+
+
+
+
+
+completedAt
+
+Kubernetes meta/v1.Time
+
+ |
+
+
+
+
+
+CompletedAt is the time at which this sensor was completed
+
+
+
+ |
+
+
+
+
+
+
+
+message string
+
+ |
+
+
+
+
+
+Message is a human readable string indicating details about a sensor in
+its phase
+
+
+
+ |
+
+
+
+
+
+
+
+nodes
+map\[string\]github.com/argoproj/argo-events/pkg/apis/sensor/v1alpha1.NodeStatus
+
+
+ |
+
+
+
+
+
+Nodes is a mapping between a node ID and the node’s status it records
+the states for the FSM of this sensor.
+
+
+
+ |
+
+
+
+
+
+
+
+triggerCycleCount int32
+
+ |
+
+
+
+
+
+TriggerCycleCount is the count of sensor’s trigger cycle runs.
+
+
+
+ |
+
+
+
+
+
+
+
+triggerCycleStatus
+ TriggerCycleState
+
+
+ |
+
+
+
+
+
+TriggerCycleState is the status from last cycle of triggers execution.
+
+
+
+ |
+
+
+
+
+
+
+
+lastCycleTime
+
+Kubernetes meta/v1.Time
+
+ |
+
+
+
+
+
+LastCycleTime is the time when last trigger cycle completed
+
+
+
+ |
+
+
+
+
+
+
+
+resources
+ SensorResources
+
+
+ |
+
+
+
+
+
+Resources refers to metadata of the resources created for the sensor
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+TimeFilter
+
+
+
+
+
+(Appears on:
+EventDependencyFilter)
+
+
+
+
+
+
+
+TimeFilter describes a window in time. DataFilters out event events that
+occur outside the time limits. In other words, only events that occur
+after Start and before Stop will pass this filter.
+
+
+
+
+
+
+
+
+
+
+
+
+
+Field
+
+ |
+
+
+
+Description
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+start string
+
+ |
+
+
+
+
+
+Start is the beginning of a time window. Before this time, events for
+this event are ignored and format is hh:mm:ss
+
+
+
+ |
+
+
+
+
+
+
+
+stop string
+
+ |
+
+
+
+
+
+StopPattern is the end of a time window. After this time, events for
+this event are ignored and format is hh:mm:ss
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+Trigger
+
+
+
+
+
+(Appears on:
+SensorSpec)
+
+
+
+
+
+
+
+Trigger is an action taken, output produced, an event created, a message
+sent
+
+
+
+
+
+
+
+
+
+
+
+
+
+Field
+
+ |
+
+
+
+Description
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+template
+ TriggerTemplate
+
+
+ |
+
+
+
+
+
+Template describes the trigger specification.
+
+
+
+ |
+
+
+
+
+
+
+
+templateParameters
+ \[\]TriggerParameter
+
+
+ |
+
+
+
+
+
+TemplateParameters is the list of resource parameters to pass to the
+template object
+
+
+
+ |
+
+
+
+
+
+
+
+resourceParameters
+ \[\]TriggerParameter
+
+
+ |
+
+
+
+
+
+ResourceParameters is the list of resource parameters to pass to
+resolved resource object in template object
+
+
+
+ |
+
+
+
+
+
+
+
+policy
+ TriggerPolicy
+
+ |
+
+
+
+
+
+Policy to configure backoff and execution criteria for the trigger
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+TriggerCondition
+
+
+
+
+
+(Appears on:
+TriggerTemplate)
+
+
+
+
+
+
+
+TriggerCondition describes condition which must be satisfied in order to
+execute a trigger. Depending upon condition type, status of dependency
+groups is used to evaluate the result.
+
+
+
+
+
+
+
+
+
+
+
+
+
+Field
+
+ |
+
+
+
+Description
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+any \[\]string
+
+ |
+
+
+
+
+
+Any acts as a OR operator between dependencies
+
+
+
+ |
+
+
+
+
+
+
+
+all \[\]string
+
+ |
+
+
+
+
+
+All acts as a AND operator between dependencies
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+TriggerCycleState (string
alias)
+
+
+
+
+
+
+
+(Appears on:
+SensorStatus)
+
+
+
+
+
+
+
+TriggerCycleState is the label for the state of the trigger cycle
+
+
+
+
+
+
+
+TriggerParameter
+
+
+
+
+
+(Appears on:
+Trigger)
+
+
+
+
+
+
+
+TriggerParameter indicates a passed parameter to a service template
+
+
+
+
+
+
+
+
+
+
+
+
+
+Field
+
+ |
+
+
+
+Description
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+src
+
+TriggerParameterSource
+
+ |
+
+
+
+
+
+Src contains a source reference to the value of the parameter from a
+event event
+
+
+
+ |
+
+
+
+
+
+
+
+dest string
+
+ |
+
+
+
+
+
+Dest is the JSONPath of a resource key. A path is a series of keys
+separated by a dot. The colon character can be escaped with ‘.’ The -1
+key can be used to append a value to an existing array. See
+https://github.com/tidwall/sjson\#path-syntax
+for more information about how this is used.
+
+
+
+ |
+
+
+
+
+
+
+
+operation
+
+TriggerParameterOperation
+
+ |
+
+
+
+
+
+Operation is what to do with the existing value at Dest, whether to
+‘prepend’, ‘overwrite’, or ‘append’ it.
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+TriggerParameterOperation (string
alias)
+
+
+
+
+
+
+
+(Appears on:
+TriggerParameter)
+
+
+
+
+
+
+
+TriggerParameterOperation represents how to set a trigger destination
+resource key
+
+
+
+
+
+
+
+TriggerParameterSource
+
+
+
+
+
+(Appears on:
+TriggerParameter)
+
+
+
+
+
+
+
+TriggerParameterSource defines the source for a parameter from a event
+event
+
+
+
+
+
+
+
+
+
+
+
+
+
+Field
+
+ |
+
+
+
+Description
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+event string
+
+ |
+
+
+
+
+
+Event is the name of the event for which to retrieve this event
+
+
+
+ |
+
+
+
+
+
+
+
+path string
+
+ |
+
+
+
+
+
+Path is the JSONPath of the event’s (JSON decoded) data key Path is a
+series of keys separated by a dot. A key may contain wildcard characters
+‘\*’ and ‘?’. To access an array value use the index as the key. The dot
+and wildcard characters can be escaped with ‘\’. See
+https://github.com/tidwall/gjson\#path-syntax
+for more information on how to use this.
+
+
+
+ |
+
+
+
+
+
+
+
+value string
+
+ |
+
+
+
+
+
+Value is the default literal value to use for this parameter source This
+is only used if the path is invalid. If the path is invalid and this is
+not defined, this param source will produce an error.
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+TriggerPolicy
+
+
+
+
+
+(Appears on:
+Trigger)
+
+
+
+
+
+
+
+TriggerPolicy dictates the policy for the trigger retries
+
+
+
+
+
+
+
+
+
+
+
+
+
+Field
+
+ |
+
+
+
+Description
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+backoff
+Backoff
+
+ |
+
+
+
+
+
+Backoff before checking resource state
+
+
+
+ |
+
+
+
+
+
+
+
+state
+ TriggerStateLabels
+
+
+ |
+
+
+
+
+
+State refers to labels used to check the resource state
+
+
+
+ |
+
+
+
+
+
+
+
+errorOnBackoffTimeout bool
+
+ |
+
+
+
+
+
+ErrorOnBackoffTimeout determines whether sensor should transition to
+error state if the backoff times out and yet the resource neither
+transitioned into success or failure.
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+TriggerStateLabels
+
+
+
+
+
+(Appears on:
+TriggerPolicy)
+
+
+
+
+
+
+
+TriggerStateLabels defines the labels used to decide if a resource is in
+success or failure state.
+
+
+
+
+
+
+
+
+
+
+
+
+
+Field
+
+ |
+
+
+
+Description
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+success map\[string\]string
+
+ |
+
+
+
+
+
+Success defines labels required to identify a resource in success state
+
+
+
+ |
+
+
+
+
+
+
+
+failure map\[string\]string
+
+ |
+
+
+
+
+
+Failure defines labels required to identify a resource in failed state
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+TriggerTemplate
+
+
+
+
+
+(Appears on:
+Trigger)
+
+
+
+
+
+
+
+TriggerTemplate is the template that describes trigger specification.
+
+
+
+
+
+
+
+
+
+
+
+
+
+Field
+
+ |
+
+
+
+Description
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+name string
+
+ |
+
+
+
+
+
+Name is a unique name of the action to take
+
+
+
+ |
+
+
+
+
+
+
+
+when
+ TriggerCondition
+
+
+ |
+
+
+
+
+
+When is the condition to execute the trigger
+
+
+
+ |
+
+
+
+
+
+
+
+GroupVersionResource
+
+Kubernetes meta/v1.GroupVersionResource
+
+ |
+
+
+
+
+
+(Members of GroupVersionResource are embedded into this
+type.)
+
+
+
+
+
+The unambiguous kind of this object - used in order to retrieve the
+appropriate kubernetes api client for this resource
+
+
+
+ |
+
+
+
+
+
+
+
+source
+ ArtifactLocation
+
+
+ |
+
+
+
+
+
+Source of the K8 resource file(s)
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+URLArtifact
+
+
+
+
+
+(Appears on:
+ArtifactLocation)
+
+
+
+
+
+
+
+URLArtifact contains information about an minio at an http endpoint.
+
+
+
+
+
+
+
+
+
+
+
+
+
+Field
+
+ |
+
+
+
+Description
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+path string
+
+ |
+
+
+
+
+
+Path is the complete URL
+
+
+
+ |
+
+
+
+
+
+
+
+verifyCert bool
+
+ |
+
+
+
+
+
+VerifyCert decides whether the connection is secure or not
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+ Generated with gen-crd-api-reference-docs
on git
+commit 8d85191
.
+
+
diff --git a/common/common.go b/common/common.go
index 305ec1a5d9..06d166e88f 100644
--- a/common/common.go
+++ b/common/common.go
@@ -17,156 +17,106 @@ limitations under the License.
package common
import (
- "github.com/argoproj/argo-events/pkg/apis/gateway"
- "github.com/argoproj/argo-events/pkg/apis/sensor"
+ "github.com/pkg/errors"
)
+// Defaults
const (
-
// ErrorResponse for http request
ErrorResponse = "Error"
-
// StandardTimeFormat is time format reference for golang
StandardTimeFormat = "2006-01-02 15:04:05"
-
// StandardYYYYMMDDFormat formats date in yyyy-mm-dd format
StandardYYYYMMDDFormat = "2006-01-02"
-
// DefaultControllerNamespace is the default namespace where the sensor and gateways controllers are installed
DefaultControllerNamespace = "argo-events"
)
-// ENV VARS
+// Environment variables
const (
// EnvVarKubeConfig is the path to the Kubernetes configuration
EnvVarKubeConfig = "KUBE_CONFIG"
-
// EnvVarDebugLog is the env var to turn on the debug mode for logging
EnvVarDebugLog = "DEBUG_LOG"
)
-// LABELS
+// Controller environment variables
const (
- // LabelOperation is a label for an operation in framework
- LabelOperation = "operation"
-
- // LabelEventSource is label for event name
- LabelEventSource = "event-source"
+ // EnvVarControllerConfigMap contains name of the configmap to retrieve controller configuration from
+ EnvVarControllerConfigMap = "CONTROLLER_CONFIG_MAP"
+ // EnvVarControllerInstanceID is used to get controller instance id
+ EnvVarControllerInstanceID = "CONTROLLER_INSTANCE_ID"
+ // EnvVarControllerName is used to get name of the controller
+ EnvVarControllerName = "CONTROLLER_NAME"
+ // EnvVarResourceName refers env var for name of the resource
+ EnvVarResourceName = "NAME"
+ // EnvVarNamespace refers to a K8s namespace
+ EnvVarNamespace = "NAMESPACE"
)
-// SENSOR CONTROLLER CONSTANTS
+// Controller labels
const (
- // env variables constants
- //LabelKeySensorControllerInstanceID is the label which allows to separate application among multiple running sensor controllers.
- LabelKeySensorControllerInstanceID = sensor.FullName + "/sensor-controller-instanceid"
-
- // LabelSensorKeyPhase is a label applied to sensors to indicate the current phase of the sensor (for filtering purposes)
- LabelSensorKeyPhase = sensor.FullName + "/phase"
-
- // LabelSensorKeyComplete is the label to mark sensors as complete
- LabelSensorKeyComplete = sensor.FullName + "/complete"
-
- // EnvVarSensorControllerConfigMap is the name of the configmap to use for the sensor-controller
- EnvVarSensorControllerConfigMap = "SENSOR_CONFIG_MAP"
-
- // labels constants
- // LabelSensorControllerName is the default deployment name of the sensor-controller
- LabelSensorControllerName = "sensor-controller"
-
- LabelArgoEventsSensorVersion = "argo-events-sensor-version"
+ // LabelGatewayName is the label for the K8s resource name
+ LabelResourceName = "resource-name"
+ // LabelControllerName is th label for the controller name
+ LabelControllerName = "controller-name"
+)
- // SensorControllerConfigMapKey is the key in the configmap to retrieve sensor configuration from.
+const (
+ // GatewayControllerConfigMapKey is the key in the configmap to retrieve controller configuration from.
// Content encoding is expected to be YAML.
- SensorControllerConfigMapKey = "config"
-
- // miscellaneous constants
- // AnnotationSensorResourceSpecHashName is the annotation of a sensor resource spec hash
- AnnotationSensorResourceSpecHashName = sensor.FullName + "/resource-spec-hash"
+ ControllerConfigMapKey = "config"
)
-// SENSOR CONSTANTS
+// Sensor constants
const (
// SensorServiceEndpoint is the endpoint to dispatch the event to
SensorServiceEndpoint = "/"
-
// SensorName refers env var for name of sensor
SensorName = "SENSOR_NAME"
-
// SensorNamespace is used to get namespace where sensors are deployed
SensorNamespace = "SENSOR_NAMESPACE"
-
// LabelSensorName is label for sensor name
LabelSensorName = "sensor-name"
-
- // EnvVarSensorControllerInstanceID is used to get sensor controller instance id
- EnvVarSensorControllerInstanceID = "SENSOR_CONTROLLER_INSTANCE_ID"
-)
-
-// GATEWAY CONTROLLER CONSTANTS
-const (
- // env variables
- // EnvVarGatewayControllerConfigMap contains name of the configmap to retrieve gateway-controller configuration from
- EnvVarGatewayControllerConfigMap = "GATEWAY_CONTROLLER_CONFIG_MAP"
-
- // EnvVarGatewayControllerInstanceID is used to get gateway controller instance id
- EnvVarGatewayControllerInstanceID = "GATEWAY_CONTROLLER_INSTANCE_ID"
-
- // EnvVarGatewayControllerName is used to get name of gateway controller
- EnvVarGatewayControllerName = "GATEWAY_CONTROLLER_NAME"
-
- // EnvVarGatewayName refers env var for name of gateway
- EnvVarGatewayName = "GATEWAY_NAME"
-
- // EnvVarGatewayNamespace is namespace where gateway controller is deployed
- EnvVarGatewayNamespace = "GATEWAY_NAMESPACE"
-
- // labels
- // LabelGatewayControllerName is the default deployment name of the gateway-controller-controller
- LabelGatewayControllerName = "gateway-controller"
-
- //LabelKeyGatewayControllerInstanceID is the label which allows to separate application among multiple running gateway-controller controllers.
- LabelKeyGatewayControllerInstanceID = gateway.FullName + "/gateway-controller-instanceid"
-
- // LabelGatewayKeyPhase is a label applied to gateways to indicate the current phase of the gateway-controller (for filtering purposes)
- LabelGatewayKeyPhase = gateway.FullName + "/phase"
-
- // LabelGatewayName is the label for gateway name
- LabelGatewayName = "gateway-name"
-
- // LabelArgoEventsGatewayVersion is the label for the gateway version
- LabelArgoEventsGatewayVersion = "argo-events-gateway-version"
-
- // AnnotationGatewayResourceSpecHashName is the annotation of a gateway resource spec hash
- AnnotationGatewayResourceSpecHashName = gateway.FullName + "/resource-spec-hash"
-
- // GatewayControllerConfigMapKey is the key in the configmap to retrieve gateway-controller configuration from.
- // Content encoding is expected to be YAML.
- GatewayControllerConfigMapKey = "config"
)
-// GATEWAY CONSTANTS
+// Gateway constants
const (
- // LabelGatewayEventSourceName is the label for a event source in gateway
- LabelGatewayEventSourceName = "event-source-name"
-
- // LabelGatewayEventSourceID is the label for gateway configuration ID
- LabelGatewayEventSourceID = "event-source-id"
-
- // LabelArgoEventsEventSourceVersion is the label for event source version
- LabelArgoEventsEventSourceVersion = "argo-events-event-source-version"
-
- // EnvVarGatewayEventSourceConfigMap is used to get map containing event sources to run in a gateway
- EnvVarGatewayEventSourceConfigMap = "GATEWAY_EVENT_SOURCE_CONFIG_MAP"
-
+ // LabelEventSourceName is the label for a event source in gateway
+ LabelEventSourceName = "event-source-name"
+ // LabelEventSourceID is the label for gateway configuration ID
+ LabelEventSourceID = "event-source-id"
EnvVarGatewayServerPort = "GATEWAY_SERVER_PORT"
-
// Server Connection Timeout, 10 seconds
ServerConnTimeout = 10
)
+const (
+ // EnvVarEventSource refers to event source name
+ EnvVarEventSource = "EVENT_SOURCE"
+ // AnnotationResourceSpecHash is the annotation of a K8s resource spec hash
+ AnnotationResourceSpecHash = "resource-spec-hash"
+)
+
// CloudEvents constants
const (
// CloudEventsVersion is the version of the CloudEvents spec targeted+
// by this library.
CloudEventsVersion = "0.1"
)
+
+var (
+ ErrNilEventSource = errors.New("event source can't be nil")
+)
+
+// Miscellaneous Labels
+const (
+ // LabelOperation is a label for an operation in framework
+ LabelOperation = "operation"
+ // LabelEventSource is label for event name
+ LabelEventSource = "event-source"
+ // LabelOwnerName is the label for resource owner name
+ LabelOwnerName = "owner-name"
+ // LabelObjectName is the label for object name
+ LabelObjectName = "object-name"
+)
diff --git a/common/logger.go b/common/logger.go
index 3736e2314e..7666c8877e 100644
--- a/common/logger.go
+++ b/common/logger.go
@@ -23,21 +23,22 @@ import (
// Logger constants
const (
- LabelNamespace = "namespace"
- LabelPhase = "phase"
- LabelInstanceID = "instance-id"
- LabelPodName = "pod-name"
- LabelServiceName = "svc-name"
- LabelEndpoint = "endpoint"
- LabelPort = "port"
- LabelURL = "url"
- LabelNodeName = "node-name"
- LabelNodeType = "node-type"
- LabelHTTPMethod = "http-method"
- LabelClientID = "client-id"
- LabelVersion = "version"
- LabelTime = "time"
- LabelTriggerName = "trigger-name"
+ LabelNamespace = "namespace"
+ LabelPhase = "phase"
+ LabelInstanceID = "instance-id"
+ LabelPodName = "pod-name"
+ LabelDeploymentName = "deployment-name"
+ LabelServiceName = "svc-name"
+ LabelEndpoint = "endpoint"
+ LabelPort = "port"
+ LabelURL = "url"
+ LabelNodeName = "node-name"
+ LabelNodeType = "node-type"
+ LabelHTTPMethod = "http-method"
+ LabelClientID = "client-id"
+ LabelVersion = "version"
+ LabelTime = "time"
+ LabelTriggerName = "trigger-name"
)
// NewArgoEventsLogger returns a new ArgoEventsLogger
diff --git a/common/util.go b/common/util.go
index f39d292331..9a2c89b648 100644
--- a/common/util.go
+++ b/common/util.go
@@ -21,8 +21,8 @@ import (
"fmt"
"hash/fnv"
"net/http"
+ "strings"
- corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/discovery"
@@ -35,14 +35,9 @@ func DefaultConfigMapName(controllerName string) string {
return fmt.Sprintf("%s-configmap", controllerName)
}
-// DefaultServiceName returns a formulated name for a service
-func DefaultServiceName(serviceName string) string {
- return fmt.Sprintf("%s-svc", serviceName)
-}
-
// ServiceDNSName returns a formulated dns name for a service
func ServiceDNSName(serviceName, namespace string) string {
- return fmt.Sprintf("%s-svc.%s.svc.cluster.local", serviceName, namespace)
+ return fmt.Sprintf("%s.%s.svc.cluster.local", serviceName, namespace)
}
// DefaultEventSourceName returns a formulated name for a gateway configuration
@@ -111,12 +106,19 @@ func GetObjectHash(obj metav1.Object) (string, error) {
return Hasher(string(b)), nil
}
-func CheckEventSourceVersion(cm *corev1.ConfigMap) error {
- if cm.Labels == nil {
- return fmt.Errorf("labels can't be empty. event source must be specified in as %s label", LabelArgoEventsEventSourceVersion)
+// FormatEndpoint returns a formatted api endpoint
+func FormatEndpoint(endpoint string) string {
+ if !strings.HasPrefix(endpoint, "/") {
+ return fmt.Sprintf("/%s", endpoint)
}
- if _, ok := cm.Labels[LabelArgoEventsEventSourceVersion]; !ok {
- return fmt.Errorf("event source must be specified in as %s label", LabelArgoEventsEventSourceVersion)
- }
- return nil
+ return endpoint
+}
+
+// FormattedURL returns a formatted url
+func FormattedURL(url, endpoint string) string {
+ return fmt.Sprintf("%s%s", url, FormatEndpoint(endpoint))
+}
+
+func ErrEventSourceTypeMismatch(eventSourceType string) string {
+ return fmt.Sprintf("event source is not type of %s", eventSourceType)
}
diff --git a/common/util_test.go b/common/util_test.go
index 7d3592b875..aded244489 100644
--- a/common/util_test.go
+++ b/common/util_test.go
@@ -17,7 +17,6 @@ limitations under the License.
package common
import (
- "fmt"
"github.com/smartystreets/goconvey/convey"
"net/http"
"testing"
@@ -66,12 +65,6 @@ func TestDefaultConfigMapName(t *testing.T) {
assert.Equal(t, "sensor-controller-configmap", res)
}
-func TestDefaultServiceName(t *testing.T) {
- convey.Convey("Given a service, get the default name", t, func() {
- convey.So(DefaultServiceName("default"), convey.ShouldEqual, fmt.Sprintf("%s-svc", "default"))
- })
-}
-
func TestDefaultNatsQueueName(t *testing.T) {
convey.Convey("Given a nats queue, get the default name", t, func() {
convey.So(DefaultNatsQueueName("default"), convey.ShouldEqual, "default-queue")
@@ -114,3 +107,15 @@ func TestServerResourceForGroupVersionKind(t *testing.T) {
})
})
}
+
+func TestFormatWebhookEndpoint(t *testing.T) {
+ convey.Convey("Given a webhook endpoint, format it", t, func() {
+ convey.So(FormatEndpoint("hello"), convey.ShouldEqual, "/hello")
+ })
+}
+
+func TestGenerateFormattedURL(t *testing.T) {
+ convey.Convey("Given a webhook, generate formatted URL", t, func() {
+ convey.So(FormattedURL("test-url", "fake"), convey.ShouldEqual, "test-url/fake")
+ })
+}
diff --git a/controllers/common/informer.go b/controllers/common/informer.go
deleted file mode 100644
index 73d7de5a65..0000000000
--- a/controllers/common/informer.go
+++ /dev/null
@@ -1,86 +0,0 @@
-package common
-
-import (
- "fmt"
-
- "k8s.io/apimachinery/pkg/api/meta"
- "k8s.io/apimachinery/pkg/runtime/schema"
- "k8s.io/client-go/informers"
- informersv1 "k8s.io/client-go/informers/core/v1"
- "k8s.io/client-go/tools/cache"
- "k8s.io/client-go/util/workqueue"
-)
-
-// ArgoEventInformerFactory holds values to create SharedInformerFactory of argo-events
-type ArgoEventInformerFactory struct {
- OwnerGroupVersionKind schema.GroupVersionKind
- OwnerInformer cache.SharedIndexInformer
- informers.SharedInformerFactory
- Queue workqueue.RateLimitingInterface
-}
-
-// NewPodInformer returns a PodInformer of argo-events
-func (c *ArgoEventInformerFactory) NewPodInformer() informersv1.PodInformer {
- podInformer := c.SharedInformerFactory.Core().V1().Pods()
- podInformer.Informer().AddEventHandler(
- cache.ResourceEventHandlerFuncs{
- DeleteFunc: func(obj interface{}) {
- owner, err := c.getOwner(obj)
- if err != nil {
- return
- }
- key, err := cache.MetaNamespaceKeyFunc(owner)
- if err == nil {
- c.Queue.Add(key)
- }
- },
- },
- )
- return podInformer
-}
-
-// NewServiceInformer returns a ServiceInformer of argo-events
-func (c *ArgoEventInformerFactory) NewServiceInformer() informersv1.ServiceInformer {
- svcInformer := c.SharedInformerFactory.Core().V1().Services()
- svcInformer.Informer().AddEventHandler(
- cache.ResourceEventHandlerFuncs{
- DeleteFunc: func(obj interface{}) {
- owner, err := c.getOwner(obj)
- if err != nil {
- return
- }
- key, err := cache.MetaNamespaceKeyFunc(owner)
- if err == nil {
- c.Queue.Add(key)
- }
- },
- },
- )
- return svcInformer
-}
-
-func (c *ArgoEventInformerFactory) getOwner(obj interface{}) (interface{}, error) {
- m, err := meta.Accessor(obj)
- if err != nil {
- return nil, err
- }
- for _, owner := range m.GetOwnerReferences() {
-
- if owner.APIVersion == c.OwnerGroupVersionKind.GroupVersion().String() &&
- owner.Kind == c.OwnerGroupVersionKind.Kind {
- key := owner.Name
- if len(m.GetNamespace()) > 0 {
- key = m.GetNamespace() + "/" + key
- }
- obj, exists, err := c.OwnerInformer.GetIndexer().GetByKey(key)
- if err != nil {
- return nil, err
- }
- if !exists {
- return nil, fmt.Errorf("failed to get object from cache")
- }
- return obj, nil
- }
- }
- return nil, fmt.Errorf("no owner found")
-}
diff --git a/controllers/common/informer_test.go b/controllers/common/informer_test.go
deleted file mode 100644
index 7b7f177b02..0000000000
--- a/controllers/common/informer_test.go
+++ /dev/null
@@ -1,191 +0,0 @@
-package common
-
-import (
- "fmt"
- "math/rand"
- "testing"
- "time"
-
- "github.com/smartystreets/goconvey/convey"
- corev1 "k8s.io/api/core/v1"
- metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
- "k8s.io/apimachinery/pkg/runtime"
- "k8s.io/apimachinery/pkg/runtime/schema"
- "k8s.io/client-go/informers"
- "k8s.io/client-go/kubernetes"
- "k8s.io/client-go/kubernetes/fake"
- "k8s.io/client-go/tools/cache"
- "k8s.io/client-go/util/workqueue"
-)
-
-func getFakePodSharedIndexInformer(clientset kubernetes.Interface) cache.SharedIndexInformer {
- // NewListWatchFromClient doesn't work with fake client.
- // ref: https://github.com/kubernetes/client-go/issues/352
- return cache.NewSharedIndexInformer(&cache.ListWatch{
- ListFunc: func(options metav1.ListOptions) (runtime.Object, error) {
- return clientset.CoreV1().Pods("").List(options)
- },
- WatchFunc: clientset.CoreV1().Pods("").Watch,
- }, &corev1.Pod{}, 1*time.Second, cache.Indexers{})
-}
-
-func getInformerFactory(clientset kubernetes.Interface, queue workqueue.RateLimitingInterface, done chan struct{}) *ArgoEventInformerFactory {
- informerFactory := informers.NewSharedInformerFactory(clientset, 0)
- ownerInformer := getFakePodSharedIndexInformer(clientset)
- go ownerInformer.Run(done)
- return &ArgoEventInformerFactory{
- OwnerGroupVersionKind: schema.GroupVersionKind{Version: "v1", Kind: "Pod"},
- OwnerInformer: ownerInformer,
- SharedInformerFactory: informerFactory,
- Queue: queue,
- }
-}
-
-func getCommonPodSpec() corev1.PodSpec {
- return corev1.PodSpec{
- Containers: []corev1.Container{
- {
- Name: "whalesay",
- Image: "docker/whalesay:latest",
- },
- },
- }
-}
-
-func getPod(owner *corev1.Pod) *corev1.Pod {
- var ownerReferneces = []metav1.OwnerReference{}
- if owner != nil {
- ownerReferneces = append(ownerReferneces, *metav1.NewControllerRef(owner, owner.GroupVersionKind()))
- }
- return &corev1.Pod{
- TypeMeta: metav1.TypeMeta{
- Kind: "Pod",
- APIVersion: "v1",
- },
- ObjectMeta: metav1.ObjectMeta{
- Name: fmt.Sprintf("pod-%d", rand.Uint32()),
- OwnerReferences: ownerReferneces,
- },
- Spec: getCommonPodSpec(),
- }
-}
-
-func getService(owner *corev1.Pod) *corev1.Service {
- var ownerReferneces = []metav1.OwnerReference{}
- if owner != nil {
- ownerReferneces = append(ownerReferneces, *metav1.NewControllerRef(owner, owner.GroupVersionKind()))
- }
- return &corev1.Service{
- TypeMeta: metav1.TypeMeta{
- Kind: "Service",
- APIVersion: "v1",
- },
- ObjectMeta: metav1.ObjectMeta{
- Name: fmt.Sprintf("svc-%d", rand.Uint32()),
- OwnerReferences: ownerReferneces,
- },
- Spec: corev1.ServiceSpec{},
- }
-}
-
-func TestNewPodInformer(t *testing.T) {
- done := make(chan struct{})
- queue := workqueue.NewRateLimitingQueue(workqueue.DefaultControllerRateLimiter())
- defer queue.ShutDown()
- namespace := "namespace"
- clientset := fake.NewSimpleClientset()
- factory := getInformerFactory(clientset, queue, done)
- convey.Convey("Given an informer factory", t, func() {
- convey.Convey("Get a new gateway pod informer and make sure its not nil", func() {
- podInformer := factory.NewPodInformer()
- convey.So(podInformer, convey.ShouldNotBeNil)
-
- convey.Convey("Handle event", func() {
- go podInformer.Informer().Run(done)
- ownerPod := getPod(nil)
- ownerPod, err := clientset.CoreV1().Pods(namespace).Create(ownerPod)
- convey.So(err, convey.ShouldBeNil)
- cache.WaitForCacheSync(done, factory.OwnerInformer.HasSynced)
-
- convey.Convey("Not enqueue owner key on creation", func() {
- pod := getPod(ownerPod)
- pod, err := clientset.CoreV1().Pods(namespace).Create(pod)
- convey.So(err, convey.ShouldBeNil)
- cache.WaitForCacheSync(done, podInformer.Informer().HasSynced)
-
- convey.So(queue.Len(), convey.ShouldEqual, 0)
-
- convey.Convey("Not enqueue owner key on update", func() {
- pod.Labels = map[string]string{"foo": "bar"}
- _, err = clientset.CoreV1().Pods(namespace).Update(pod)
- convey.So(err, convey.ShouldBeNil)
- cache.WaitForCacheSync(done, podInformer.Informer().HasSynced)
-
- convey.So(queue.Len(), convey.ShouldEqual, 0)
- })
-
- convey.Convey("Enqueue owner key on deletion", func() {
- err := clientset.CoreV1().Pods(namespace).Delete(pod.Name, &metav1.DeleteOptions{})
- convey.So(err, convey.ShouldBeNil)
- cache.WaitForCacheSync(done, podInformer.Informer().HasSynced)
-
- convey.So(queue.Len(), convey.ShouldEqual, 1)
- key, _ := queue.Get()
- queue.Done(key)
- convey.So(key, convey.ShouldEqual, fmt.Sprintf("%s/%s", namespace, ownerPod.Name))
- })
- })
- })
- })
- })
-}
-
-func TestNewServiceInformer(t *testing.T) {
- done := make(chan struct{})
- queue := workqueue.NewRateLimitingQueue(workqueue.DefaultControllerRateLimiter())
- defer queue.ShutDown()
- namespace := "namespace"
- clientset := fake.NewSimpleClientset()
- factory := getInformerFactory(clientset, queue, done)
- convey.Convey("Given an informer factory", t, func() {
- convey.Convey("Get a new gateway service informer and make sure its not nil", func() {
- svcInformer := factory.NewServiceInformer()
- convey.So(svcInformer, convey.ShouldNotBeNil)
-
- convey.Convey("Handle event", func() {
- go svcInformer.Informer().Run(done)
- ownerPod := getPod(nil)
- ownerPod, err := clientset.CoreV1().Pods(namespace).Create(ownerPod)
- convey.So(err, convey.ShouldBeNil)
- cache.WaitForCacheSync(done, factory.OwnerInformer.HasSynced)
-
- convey.Convey("Not enqueue owner key on creation", func() {
- service := getService(ownerPod)
- service, err := clientset.CoreV1().Services(namespace).Create(service)
- convey.So(err, convey.ShouldBeNil)
- cache.WaitForCacheSync(done, svcInformer.Informer().HasSynced)
- convey.So(queue.Len(), convey.ShouldEqual, 0)
-
- convey.Convey("Not enqueue owner key on update", func() {
- service.Labels = map[string]string{"foo": "bar"}
- service, err = clientset.CoreV1().Services(namespace).Update(service)
- convey.So(err, convey.ShouldBeNil)
- cache.WaitForCacheSync(done, svcInformer.Informer().HasSynced)
- convey.So(queue.Len(), convey.ShouldEqual, 0)
- })
-
- convey.Convey("Enqueue owner key on deletion", func() {
- err := clientset.CoreV1().Services(namespace).Delete(service.Name, &metav1.DeleteOptions{})
- convey.So(err, convey.ShouldBeNil)
- cache.WaitForCacheSync(done, svcInformer.Informer().HasSynced)
-
- convey.So(queue.Len(), convey.ShouldEqual, 1)
- key, _ := queue.Get()
- queue.Done(key)
- convey.So(key, convey.ShouldEqual, fmt.Sprintf("%s/%s", namespace, ownerPod.Name))
- })
- })
- })
- })
- })
-}
diff --git a/controllers/common/util.go b/controllers/common/util.go
index 112335890c..a7ad13462a 100644
--- a/controllers/common/util.go
+++ b/controllers/common/util.go
@@ -1,39 +1,50 @@
+/*
+Copyright 2018 BlackRock, Inc.
+
+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.
+*/
+
package common
import (
"github.com/argoproj/argo-events/common"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime/schema"
+ "k8s.io/apimachinery/pkg/selection"
)
-// ChildResourceContext holds necessary information for child resource setup
-type ChildResourceContext struct {
- SchemaGroupVersionKind schema.GroupVersionKind
- LabelOwnerName string
- LabelKeyOwnerControllerInstanceID string
- AnnotationOwnerResourceHashName string
- InstanceID string
-}
-
// SetObjectMeta sets ObjectMeta of child resource
-func (ctx *ChildResourceContext) SetObjectMeta(owner, obj metav1.Object) error {
+func SetObjectMeta(owner, obj metav1.Object, gvk schema.GroupVersionKind) error {
references := obj.GetOwnerReferences()
references = append(references,
- *metav1.NewControllerRef(owner, ctx.SchemaGroupVersionKind),
+ *metav1.NewControllerRef(owner, gvk),
)
obj.SetOwnerReferences(references)
if obj.GetName() == "" && obj.GetGenerateName() == "" {
- obj.SetGenerateName(owner.GetName())
+ obj.SetName(owner.GetName())
+ }
+ if obj.GetNamespace() == "" {
+ obj.SetNamespace(owner.GetNamespace())
}
- labels := obj.GetLabels()
- if labels == nil {
- labels = make(map[string]string)
+ objLabels := obj.GetLabels()
+ if objLabels == nil {
+ objLabels = make(map[string]string)
}
- labels[ctx.LabelOwnerName] = owner.GetName()
- labels[ctx.LabelKeyOwnerControllerInstanceID] = ctx.InstanceID
- obj.SetLabels(labels)
+ objLabels[common.LabelOwnerName] = owner.GetName()
+ obj.SetLabels(objLabels)
hash, err := common.GetObjectHash(obj)
if err != nil {
@@ -43,8 +54,17 @@ func (ctx *ChildResourceContext) SetObjectMeta(owner, obj metav1.Object) error {
if annotations == nil {
annotations = make(map[string]string)
}
- annotations[ctx.AnnotationOwnerResourceHashName] = hash
+ annotations[common.AnnotationResourceSpecHash] = hash
obj.SetAnnotations(annotations)
return nil
}
+
+// OwnerLabelSelector returns label selector for a K8s resource by it's owner
+func OwnerLabelSelector(ownerName string) (labels.Selector, error) {
+ req, err := labels.NewRequirement(common.LabelResourceName, selection.Equals, []string{ownerName})
+ if err != nil {
+ return nil, err
+ }
+ return labels.NewSelector().Add(*req), nil
+}
diff --git a/controllers/common/util_test.go b/controllers/common/util_test.go
index 012e07eb9c..1f7526c22e 100644
--- a/controllers/common/util_test.go
+++ b/controllers/common/util_test.go
@@ -1,39 +1,49 @@
+/*
+Copyright 2018 BlackRock, Inc.
+
+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.
+*/
+
package common
import (
"testing"
- "github.com/smartystreets/goconvey/convey"
+ "github.com/argoproj/argo-events/common"
+ "github.com/stretchr/testify/assert"
+ appv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
- "k8s.io/apimachinery/pkg/runtime/schema"
)
func TestSetObjectMeta(t *testing.T) {
- convey.Convey("Given an object, set meta", t, func() {
- groupVersionKind := schema.GroupVersionKind{
- Group: "grp",
- Version: "ver",
- Kind: "kind",
- }
- ctx := ChildResourceContext{
- SchemaGroupVersionKind: groupVersionKind,
- LabelOwnerName: "foo",
- LabelKeyOwnerControllerInstanceID: "id",
- AnnotationOwnerResourceHashName: "hash",
- InstanceID: "ID",
- }
- owner := corev1.Pod{}
- pod := corev1.Pod{}
- ref := metav1.NewControllerRef(&owner, groupVersionKind)
-
- err := ctx.SetObjectMeta(&owner, &pod)
- convey.So(err, convey.ShouldBeEmpty)
- convey.So(pod.Labels["foo"], convey.ShouldEqual, "")
- convey.So(pod.Labels["id"], convey.ShouldEqual, "ID")
- convey.So(pod.Annotations, convey.ShouldContainKey, "hash")
- convey.So(pod.Name, convey.ShouldEqual, "")
- convey.So(pod.GenerateName, convey.ShouldEqual, "")
- convey.So(pod.OwnerReferences, convey.ShouldContain, *ref)
- })
+ owner := appv1.Deployment{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "fake-deployment",
+ Namespace: "fake-namespace",
+ },
+ }
+ pod := corev1.Pod{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "fake-pod",
+ },
+ }
+
+ err := SetObjectMeta(&owner, &pod, owner.GroupVersionKind())
+ assert.Nil(t, err)
+ assert.Equal(t, "fake-namespace", pod.Namespace)
+ assert.Equal(t, owner.GroupVersionKind().Kind, pod.OwnerReferences[0].Kind)
+ assert.NotEmpty(t, pod.Annotations[common.AnnotationResourceSpecHash])
+ assert.NotEmpty(t, pod.Labels)
+ assert.Equal(t, owner.Name, pod.Labels[common.LabelOwnerName])
}
diff --git a/cmd/controllers/gateway/main.go b/controllers/gateway/cmd/main.go
similarity index 82%
rename from cmd/controllers/gateway/main.go
rename to controllers/gateway/cmd/main.go
index c489d84054..be79cdd645 100644
--- a/cmd/controllers/gateway/main.go
+++ b/controllers/gateway/cmd/main.go
@@ -33,12 +33,12 @@ func main() {
}
// gateway-controller configuration
- configMap, ok := os.LookupEnv(common.EnvVarGatewayControllerConfigMap)
+ configMap, ok := os.LookupEnv(common.EnvVarControllerConfigMap)
if !ok {
- configMap = common.DefaultConfigMapName(common.LabelGatewayControllerName)
+ panic("controller configmap is not provided")
}
- namespace, ok := os.LookupEnv(common.EnvVarGatewayNamespace)
+ namespace, ok := os.LookupEnv(common.EnvVarNamespace)
if !ok {
namespace = common.DefaultControllerNamespace
}
@@ -51,6 +51,6 @@ func main() {
panic(err)
}
- go controller.Run(context.Background(), 1, 1)
+ go controller.Run(context.Background(), 1)
select {}
}
diff --git a/gateways/community/gcp-pubsub/config_test.go b/controllers/gateway/common.go
similarity index 54%
rename from gateways/community/gcp-pubsub/config_test.go
rename to controllers/gateway/common.go
index f816c79722..a8d22c43ec 100644
--- a/gateways/community/gcp-pubsub/config_test.go
+++ b/controllers/gateway/common.go
@@ -14,24 +14,14 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
-package pubsub
+package gateway
-import (
- "github.com/smartystreets/goconvey/convey"
- "testing"
-)
-
-var es = `
-projectID: "1234"
-topic: "test"
-`
+import "github.com/argoproj/argo-events/pkg/apis/gateway"
-func TestParseConfig(t *testing.T) {
- convey.Convey("Given a gcp-pubsub event source, parse it", t, func() {
- ps, err := parseEventSource(es)
- convey.So(err, convey.ShouldBeNil)
- convey.So(ps, convey.ShouldNotBeNil)
- _, ok := ps.(*pubSubEventSource)
- convey.So(ok, convey.ShouldEqual, true)
- })
-}
+// Labels
+const (
+ //LabelKeyGatewayControllerInstanceID is the label which allows to separate application among multiple running controller controllers.
+ LabelControllerInstanceID = gateway.FullName + "/gateway-controller-instanceid"
+ // LabelGatewayKeyPhase is a label applied to gateways to indicate the current phase of the controller (for filtering purposes)
+ LabelPhase = gateway.FullName + "/phase"
+)
diff --git a/controllers/gateway/config.go b/controllers/gateway/config.go
index 9dddaf0518..6d32e15398 100644
--- a/controllers/gateway/config.go
+++ b/controllers/gateway/config.go
@@ -22,6 +22,7 @@ import (
"github.com/argoproj/argo-events/common"
"github.com/ghodss/yaml"
+ "github.com/pkg/errors"
apiv1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/fields"
@@ -31,8 +32,8 @@ import (
)
// watches configuration for gateway controller
-func (c *GatewayController) watchControllerConfigMap(ctx context.Context) (cache.Controller, error) {
- c.log.Info("watching gateway-controller config map updates")
+func (c *Controller) watchControllerConfigMap(ctx context.Context) (cache.Controller, error) {
+ c.logger.Infoln("watching gateway-controller config map updates")
source := c.newControllerConfigMapWatch()
_, controller := cache.NewInformer(
source,
@@ -41,19 +42,19 @@ func (c *GatewayController) watchControllerConfigMap(ctx context.Context) (cache
cache.ResourceEventHandlerFuncs{
AddFunc: func(obj interface{}) {
if cm, ok := obj.(*apiv1.ConfigMap); ok {
- c.log.Info("detected EventSource update. updating the gateway-controller config.")
+ c.logger.Info("detected new gateway controller configmap")
err := c.updateConfig(cm)
if err != nil {
- c.log.Error("update of config failed", "err", err)
+ c.logger.WithError(err).Errorln("update of gateway controller configuration failed")
}
}
},
UpdateFunc: func(old, new interface{}) {
if newCm, ok := new.(*apiv1.ConfigMap); ok {
- c.log.Info("detected EventSource update. updating the gateway-controller config.")
+ c.logger.Infoln("detected gateway controller configmap update.")
err := c.updateConfig(newCm)
if err != nil {
- c.log.WithError(err).Error("update of config failed")
+ c.logger.WithError(err).Errorln("update of gateway controller configuration failed")
}
}
},
@@ -64,8 +65,8 @@ func (c *GatewayController) watchControllerConfigMap(ctx context.Context) (cache
}
// creates a new config map watcher
-func (c *GatewayController) newControllerConfigMapWatch() *cache.ListWatch {
- x := c.kubeClientset.CoreV1().RESTClient()
+func (c *Controller) newControllerConfigMapWatch() *cache.ListWatch {
+ x := c.k8sClient.CoreV1().RESTClient()
resource := "configmaps"
name := c.ConfigMap
fieldSelector := fields.ParseSelectorOrDie(fmt.Sprintf("metadata.name=%s", name))
@@ -91,8 +92,8 @@ func (c *GatewayController) newControllerConfigMapWatch() *cache.ListWatch {
}
// ResyncConfig reloads the gateway-controller config from the configmap
-func (c *GatewayController) ResyncConfig(namespace string) error {
- cmClient := c.kubeClientset.CoreV1().ConfigMaps(namespace)
+func (c *Controller) ResyncConfig(namespace string) error {
+ cmClient := c.k8sClient.CoreV1().ConfigMaps(namespace)
cm, err := cmClient.Get(c.ConfigMap, metav1.GetOptions{})
if err != nil {
return err
@@ -101,12 +102,12 @@ func (c *GatewayController) ResyncConfig(namespace string) error {
}
// updates the gateway controller configuration
-func (c *GatewayController) updateConfig(cm *apiv1.ConfigMap) error {
- configStr, ok := cm.Data[common.GatewayControllerConfigMapKey]
+func (c *Controller) updateConfig(cm *apiv1.ConfigMap) error {
+ configStr, ok := cm.Data[common.ControllerConfigMapKey]
if !ok {
- return fmt.Errorf("configMap '%s' does not have key '%s'", c.ConfigMap, common.GatewayControllerConfigMapKey)
+ return errors.Errorf("configMap '%s' does not have key '%s'", c.ConfigMap, common.ControllerConfigMapKey)
}
- var config GatewayControllerConfig
+ var config ControllerConfig
err := yaml.Unmarshal([]byte(configStr), &config)
if err != nil {
return err
diff --git a/controllers/gateway/config_test.go b/controllers/gateway/config_test.go
index 508e7d2e2c..5257e7bd74 100644
--- a/controllers/gateway/config_test.go
+++ b/controllers/gateway/config_test.go
@@ -20,7 +20,7 @@ import (
"testing"
"github.com/argoproj/argo-events/common"
- "github.com/smartystreets/goconvey/convey"
+ "github.com/stretchr/testify/assert"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
@@ -29,47 +29,24 @@ var (
configmapName = common.DefaultConfigMapName("gateway-controller")
)
-func TestGatewayControllerConfigWatch(t *testing.T) {
- gc := getGatewayController()
-
- convey.Convey("Given a gateway", t, func() {
- convey.Convey("Create a new watch and make sure watcher is not nil", func() {
- watcher := gc.newControllerConfigMapWatch()
- convey.So(watcher, convey.ShouldNotBeNil)
- })
- })
-
- convey.Convey("Given a gateway, resync config", t, func() {
- convey.Convey("Update a gateway configmap with new instance id and remove namespace", func() {
- cmObj := &corev1.ConfigMap{
- ObjectMeta: metav1.ObjectMeta{
- Namespace: common.DefaultControllerNamespace,
- Name: gc.ConfigMap,
- },
- Data: map[string]string{
- common.GatewayControllerConfigMapKey: `instanceID: fake-instance-id`,
- },
- }
- cm, err := gc.kubeClientset.CoreV1().ConfigMaps(gc.Namespace).Create(cmObj)
- convey.Convey("Make sure no error occurs", func() {
- convey.So(err, convey.ShouldBeNil)
-
- convey.Convey("Updated gateway configmap must be non-nil", func() {
- convey.So(cm, convey.ShouldNotBeNil)
-
- convey.Convey("Resync the gateway configuration", func() {
- err := gc.ResyncConfig(cmObj.Namespace)
- convey.Convey("No error should occur while resyncing gateway configuration", func() {
- convey.So(err, convey.ShouldBeNil)
-
- convey.Convey("The updated instance id must be fake-instance-id", func() {
- convey.So(gc.Config.InstanceID, convey.ShouldEqual, "fake-instance-id")
- convey.So(gc.Config.Namespace, convey.ShouldBeEmpty)
- })
- })
- })
- })
- })
- })
- })
+func TestController_ResyncConfig(t *testing.T) {
+ controller := newController()
+
+ cmObj := &corev1.ConfigMap{
+ ObjectMeta: metav1.ObjectMeta{
+ Namespace: common.DefaultControllerNamespace,
+ Name: controller.ConfigMap,
+ },
+ Data: map[string]string{
+ common.ControllerConfigMapKey: `instanceID: fake-instance-id`,
+ },
+ }
+
+ cm, err := controller.k8sClient.CoreV1().ConfigMaps(controller.Namespace).Create(cmObj)
+ assert.Nil(t, err)
+ assert.NotNil(t, cm)
+ err = controller.ResyncConfig(common.DefaultControllerNamespace)
+ assert.Nil(t, err)
+ assert.Equal(t, controller.Config.Namespace, "")
+ assert.Equal(t, controller.Config.InstanceID, "fake-instance-id")
}
diff --git a/controllers/gateway/controller.go b/controllers/gateway/controller.go
index 6177283158..c5f88662a4 100644
--- a/controllers/gateway/controller.go
+++ b/controllers/gateway/controller.go
@@ -20,18 +20,14 @@ import (
"context"
"errors"
"fmt"
- "github.com/sirupsen/logrus"
"time"
base "github.com/argoproj/argo-events"
"github.com/argoproj/argo-events/common"
- ccommon "github.com/argoproj/argo-events/controllers/common"
"github.com/argoproj/argo-events/pkg/apis/gateway/v1alpha1"
clientset "github.com/argoproj/argo-events/pkg/client/gateway/clientset/versioned"
- metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
- "k8s.io/apimachinery/pkg/labels"
+ "github.com/sirupsen/logrus"
"k8s.io/apimachinery/pkg/util/wait"
- "k8s.io/client-go/informers"
informersv1 "k8s.io/client-go/informers/core/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
@@ -40,37 +36,35 @@ import (
)
const (
- gatewayResyncPeriod = 20 * time.Minute
- gatewayResourceResyncPeriod = 30 * time.Minute
- rateLimiterBaseDelay = 5 * time.Second
- rateLimiterMaxDelay = 1000 * time.Second
+ gatewayResyncPeriod = 20 * time.Minute
+ rateLimiterBaseDelay = 5 * time.Second
+ rateLimiterMaxDelay = 1000 * time.Second
)
-// GatewayControllerConfig contain the configuration settings for the gateway-controller
-type GatewayControllerConfig struct {
- // InstanceID is a label selector to limit the gateway-controller's watch of gateway jobs to a specific instance.
+// ControllerConfig contain the configuration settings for the controller
+type ControllerConfig struct {
+ // InstanceID is a label selector to limit the controller's watch of gateway to a specific instance.
InstanceID string
-
- // Namespace is a label selector filter to limit gateway-controller-controller's watch to specific namespace
+ // Namespace is a label selector filter to limit controller's watch to specific namespace
Namespace string
}
-// GatewayController listens for new gateways and hands off handling of each gateway-controller on the queue to the operator
-type GatewayController struct {
- // EventSource is the name of the config map in which to derive configuration of the contoller
+// Controller listens for new gateways and hands off handling of each gateway controller on the queue to the operator
+type Controller struct {
+ // ConfigMap is the name of the config map in which to derive configuration of the controller
ConfigMap string
- // Namespace for gateway controller
+ // Namespace for controller
Namespace string
- // Config is the gateway-controller gateway-controller-controller's configuration
- Config GatewayControllerConfig
- // log is the logger for a gateway
- log *logrus.Logger
-
- // kubernetes config and apis
- kubeConfig *rest.Config
- kubeClientset kubernetes.Interface
- gatewayClientset clientset.Interface
-
+ // Config is the controller's configuration
+ Config ControllerConfig
+ // logger to logger stuff
+ logger *logrus.Logger
+ // K8s rest config
+ kubeConfig *rest.Config
+ // k8sClient is Kubernetes client
+ k8sClient kubernetes.Interface
+ // gatewayClient is the Argo-Events gateway resource client
+ gatewayClient clientset.Interface
// gateway-controller informer and queue
podInformer informersv1.PodInformer
svcInformer informersv1.ServiceInformer
@@ -78,21 +72,22 @@ type GatewayController struct {
queue workqueue.RateLimitingInterface
}
-// NewGatewayController creates a new Controller
-func NewGatewayController(rest *rest.Config, configMap, namespace string) *GatewayController {
+// NewGatewayController creates a new controller
+func NewGatewayController(rest *rest.Config, configMap, namespace string) *Controller {
rateLimiter := workqueue.NewItemExponentialFailureRateLimiter(rateLimiterBaseDelay, rateLimiterMaxDelay)
- return &GatewayController{
- ConfigMap: configMap,
- Namespace: namespace,
- kubeConfig: rest,
- log: common.NewArgoEventsLogger(),
- kubeClientset: kubernetes.NewForConfigOrDie(rest),
- gatewayClientset: clientset.NewForConfigOrDie(rest),
- queue: workqueue.NewRateLimitingQueue(rateLimiter),
+ return &Controller{
+ ConfigMap: configMap,
+ Namespace: namespace,
+ kubeConfig: rest,
+ logger: common.NewArgoEventsLogger(),
+ k8sClient: kubernetes.NewForConfigOrDie(rest),
+ gatewayClient: clientset.NewForConfigOrDie(rest),
+ queue: workqueue.NewRateLimitingQueue(rateLimiter),
}
}
-func (c *GatewayController) processNextItem() bool {
+// processNextItem processes a gateway resource on the controller's queue
+func (c *Controller) processNextItem() bool {
// Wait until there is a new item in the queue
key, quit := c.queue.Get()
if quit {
@@ -102,26 +97,26 @@ func (c *GatewayController) processNextItem() bool {
obj, exists, err := c.informer.GetIndexer().GetByKey(key.(string))
if err != nil {
- c.log.WithField(common.LabelGatewayName, key.(string)).WithError(err).Warn("failed to get gateway from informer index")
+ c.logger.WithField(common.LabelResourceName, key.(string)).WithError(err).Warnln("failed to get gateway from informer index")
return true
}
if !exists {
- // this happens after gateway-controller was deleted, but work queue still had entry in it
+ // this happens after controller was deleted, but work queue still had entry in it
return true
}
gw, ok := obj.(*v1alpha1.Gateway)
if !ok {
- c.log.WithField(common.LabelGatewayName, key.(string)).WithError(err).Warn("key in index is not a gateway")
+ c.logger.WithField(common.LabelResourceName, key.(string)).WithError(err).Warnln("key in index is not a gateway")
return true
}
- ctx := newGatewayOperationCtx(gw, c)
+ ctx := newGatewayContext(gw, c)
err = ctx.operate()
if err != nil {
- if err := common.GenerateK8sEvent(c.kubeClientset,
+ if err := common.GenerateK8sEvent(c.k8sClient,
fmt.Sprintf("controller failed to operate on gateway %s", gw.Name),
common.StateChangeEventType,
"controller operation failed",
@@ -130,25 +125,25 @@ func (c *GatewayController) processNextItem() bool {
c.Config.InstanceID,
gw.Kind,
map[string]string{
- common.LabelGatewayName: gw.Name,
- common.LabelEventType: string(common.EscalationEventType),
+ common.LabelResourceName: gw.Name,
+ common.LabelEventType: string(common.EscalationEventType),
},
); err != nil {
- ctx.log.WithError(err).Error("failed to create K8s event to escalate controller operation failure")
+ ctx.logger.WithError(err).Errorln("failed to create K8s event to escalate controller operation failure")
}
}
err = c.handleErr(err, key)
// create k8 event to escalate the error
if err != nil {
- ctx.log.WithError(err).Error("gateway controller failed to handle error")
+ ctx.logger.WithError(err).Errorln("controller failed to handle error")
}
return true
}
// handleErr checks if an error happened and make sure we will retry later
// returns an error if unable to handle the error
-func (c *GatewayController) handleErr(err error, key interface{}) error {
+func (c *Controller) handleErr(err error, key interface{}) error {
if err == nil {
// Forget about the #AddRateLimited history of key on every successful sync
// Ensure future updates for this key are not delayed because of outdated error history
@@ -160,7 +155,7 @@ func (c *GatewayController) handleErr(err error, key interface{}) error {
// requeues will happen very quickly even after a gateway pod goes down
// we want to give the event pod a chance to come back up so we give a generous number of retries
if c.queue.NumRequeues(key) < 20 {
- c.log.WithField(common.LabelGatewayName, key.(string)).WithError(err).Error("error syncing gateway")
+ c.logger.WithField(common.LabelResourceName, key.(string)).WithError(err).Errorln("error syncing gateway")
// Re-enqueue the key rate limited. This key will be processed later again.
c.queue.AddRateLimited(key)
@@ -169,63 +164,41 @@ func (c *GatewayController) handleErr(err error, key interface{}) error {
return errors.New("exceeded max requeues")
}
-// Run executes the gateway-controller
-func (c *GatewayController) Run(ctx context.Context, gwThreads, eventThreads int) {
+// Run processes the gateway resources on the controller's queue
+func (c *Controller) Run(ctx context.Context, threads int) {
defer c.queue.ShutDown()
- c.log.WithFields(
+ c.logger.WithFields(
map[string]interface{}{
common.LabelInstanceID: c.Config.InstanceID,
common.LabelVersion: base.GetVersion().Version,
- }).Info("starting gateway-controller")
+ }).Infoln("starting controller")
+
_, err := c.watchControllerConfigMap(ctx)
if err != nil {
- c.log.WithError(err).Error("failed to register watch for gateway-controller config map")
- return
- }
-
- c.informer = c.newGatewayInformer()
- go c.informer.Run(ctx.Done())
-
- if !cache.WaitForCacheSync(ctx.Done(), c.informer.HasSynced) {
- c.log.Panicf("timed out waiting for the caches to sync for gateways")
+ c.logger.WithError(err).Errorln("failed to register watch for controller config map")
return
}
- listOptionsFunc := func(options *metav1.ListOptions) {
- labelSelector := labels.NewSelector().Add(c.instanceIDReq())
- options.LabelSelector = labelSelector.String()
- }
- factory := ccommon.ArgoEventInformerFactory{
- OwnerGroupVersionKind: v1alpha1.SchemaGroupVersionKind,
- OwnerInformer: c.informer,
- SharedInformerFactory: informers.NewFilteredSharedInformerFactory(c.kubeClientset, gatewayResourceResyncPeriod, c.Config.Namespace, listOptionsFunc),
- Queue: c.queue,
- }
-
- c.podInformer = factory.NewPodInformer()
- go c.podInformer.Informer().Run(ctx.Done())
-
- if !cache.WaitForCacheSync(ctx.Done(), c.podInformer.Informer().HasSynced) {
- c.log.Panic("timed out waiting for the caches to sync for gateway pods")
- return
+ c.informer, err = c.newGatewayInformer()
+ if err != nil {
+ panic(err)
}
- c.svcInformer = factory.NewServiceInformer()
- go c.svcInformer.Informer().Run(ctx.Done())
+ go c.informer.Run(ctx.Done())
- if !cache.WaitForCacheSync(ctx.Done(), c.svcInformer.Informer().HasSynced) {
- c.log.Panic("timed out waiting for the caches to sync for gateway services")
+ if !cache.WaitForCacheSync(ctx.Done(), c.informer.HasSynced) {
+ c.logger.Errorln("timed out waiting for the caches to sync for gateways")
return
}
- for i := 0; i < gwThreads; i++ {
+ for i := 0; i < threads; i++ {
go wait.Until(c.runWorker, time.Second, ctx.Done())
}
<-ctx.Done()
}
-func (c *GatewayController) runWorker() {
+func (c *Controller) runWorker() {
for c.processNextItem() {
}
}
diff --git a/controllers/gateway/controller_test.go b/controllers/gateway/controller_test.go
index ab9f4cb918..c4278e2a78 100644
--- a/controllers/gateway/controller_test.go
+++ b/controllers/gateway/controller_test.go
@@ -19,99 +19,67 @@ package gateway
import (
"fmt"
"testing"
- "time"
"github.com/argoproj/argo-events/common"
+ "github.com/argoproj/argo-events/pkg/apis/gateway/v1alpha1"
fakegateway "github.com/argoproj/argo-events/pkg/client/gateway/clientset/versioned/fake"
- "github.com/smartystreets/goconvey/convey"
- corev1 "k8s.io/api/core/v1"
+ "github.com/stretchr/testify/assert"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
- "k8s.io/apimachinery/pkg/runtime"
- "k8s.io/client-go/informers"
- "k8s.io/client-go/kubernetes"
"k8s.io/client-go/kubernetes/fake"
- "k8s.io/client-go/tools/cache"
"k8s.io/client-go/util/workqueue"
)
-func getFakePodSharedIndexInformer(clientset kubernetes.Interface) cache.SharedIndexInformer {
- // NewListWatchFromClient doesn't work with fake client.
- // ref: https://github.com/kubernetes/client-go/issues/352
- return cache.NewSharedIndexInformer(&cache.ListWatch{
- ListFunc: func(options metav1.ListOptions) (runtime.Object, error) {
- return clientset.CoreV1().Pods("").List(options)
- },
- WatchFunc: clientset.CoreV1().Pods("").Watch,
- }, &corev1.Pod{}, 1*time.Second, cache.Indexers{})
-}
-
-func getGatewayController() *GatewayController {
- clientset := fake.NewSimpleClientset()
- done := make(chan struct{})
- informer := getFakePodSharedIndexInformer(clientset)
- go informer.Run(done)
- factory := informers.NewSharedInformerFactory(clientset, 0)
- podInformer := factory.Core().V1().Pods()
- go podInformer.Informer().Run(done)
- svcInformer := factory.Core().V1().Services()
- go svcInformer.Informer().Run(done)
- return &GatewayController{
+func newController() *Controller {
+ controller := &Controller{
ConfigMap: configmapName,
Namespace: common.DefaultControllerNamespace,
- Config: GatewayControllerConfig{
+ Config: ControllerConfig{
Namespace: common.DefaultControllerNamespace,
InstanceID: "argo-events",
},
- kubeClientset: clientset,
- gatewayClientset: fakegateway.NewSimpleClientset(),
- podInformer: podInformer,
- svcInformer: svcInformer,
- informer: informer,
- queue: workqueue.NewRateLimitingQueue(workqueue.DefaultControllerRateLimiter()),
- log: common.NewArgoEventsLogger(),
+ k8sClient: fake.NewSimpleClientset(),
+ gatewayClient: fakegateway.NewSimpleClientset(),
+ queue: workqueue.NewRateLimitingQueue(workqueue.DefaultControllerRateLimiter()),
+ logger: common.NewArgoEventsLogger(),
}
+ informer, err := controller.newGatewayInformer()
+ if err != nil {
+ panic(err)
+ }
+ controller.informer = informer
+ return controller
}
-func TestGatewayController(t *testing.T) {
- convey.Convey("Given a gateway controller, process queue items", t, func() {
- controller := getGatewayController()
-
- convey.Convey("Create a resource queue, add new item and process it", func() {
- controller.queue = workqueue.NewRateLimitingQueue(workqueue.DefaultControllerRateLimiter())
- controller.informer = controller.newGatewayInformer()
- controller.queue.Add("hi")
- res := controller.processNextItem()
+func TestGatewayController_ProcessNextItem(t *testing.T) {
+ controller := newController()
+ controller.queue = workqueue.NewRateLimitingQueue(workqueue.DefaultControllerRateLimiter())
+ gw := &v1alpha1.Gateway{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "fake-gateway",
+ Namespace: common.DefaultControllerNamespace,
+ },
+ Spec: v1alpha1.GatewaySpec{},
+ }
+ err := controller.informer.GetIndexer().Add(gw)
+ assert.Nil(t, err)
- convey.Convey("Item from queue must be successfully processed", func() {
- convey.So(res, convey.ShouldBeTrue)
- })
+ controller.queue.Add("fake-gateway")
+ res := controller.processNextItem()
+ assert.Equal(t, res, true)
- convey.Convey("Shutdown queue and make sure queue does not process next item", func() {
- controller.queue.ShutDown()
- res := controller.processNextItem()
- convey.So(res, convey.ShouldBeFalse)
- })
- })
- })
+ controller.queue.ShutDown()
+ res = controller.processNextItem()
+ assert.Equal(t, res, false)
+}
- convey.Convey("Given a gateway controller, handle errors in queue", t, func() {
- controller := getGatewayController()
- convey.Convey("Create a resource queue and add an item", func() {
- controller.queue = workqueue.NewRateLimitingQueue(workqueue.DefaultControllerRateLimiter())
- controller.queue.Add("hi")
- convey.Convey("Handle an nil error", func() {
- err := controller.handleErr(nil, "hi")
- convey.So(err, convey.ShouldBeNil)
- })
- convey.Convey("Exceed max requeues", func() {
- controller.queue.Add("bye")
- var err error
- for i := 0; i < 21; i++ {
- err = controller.handleErr(fmt.Errorf("real error"), "bye")
- }
- convey.So(err, convey.ShouldNotBeNil)
- convey.So(err.Error(), convey.ShouldEqual, "exceeded max requeues")
- })
- })
- })
+func TestGatewayController_HandleErr(t *testing.T) {
+ controller := newController()
+ controller.queue = workqueue.NewRateLimitingQueue(workqueue.DefaultControllerRateLimiter())
+ controller.queue.Add("hi")
+ var err error
+ for i := 0; i < 21; i++ {
+ err = controller.handleErr(fmt.Errorf("real error"), "bye")
+ }
+ assert.NotNil(t, err)
+ assert.Equal(t, err.Error(), "exceeded max requeues")
}
diff --git a/controllers/gateway/informer.go b/controllers/gateway/informer.go
index e555f87ed2..45fc5302c9 100644
--- a/controllers/gateway/informer.go
+++ b/controllers/gateway/informer.go
@@ -17,48 +17,41 @@ limitations under the License.
package gateway
import (
- "github.com/argoproj/argo-events/pkg/apis/gateway/v1alpha1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/selection"
"k8s.io/client-go/tools/cache"
- "github.com/argoproj/argo-events/common"
gatewayinformers "github.com/argoproj/argo-events/pkg/client/gateway/informers/externalversions"
)
-func (c *GatewayController) instanceIDReq() labels.Requirement {
- // it makes sense to make instance id is mandatory.
+func (c *Controller) instanceIDReq() (*labels.Requirement, error) {
if c.Config.InstanceID == "" {
panic("instance id is required")
}
- instanceIDReq, err := labels.NewRequirement(common.LabelKeyGatewayControllerInstanceID, selection.Equals, []string{c.Config.InstanceID})
+ instanceIDReq, err := labels.NewRequirement(LabelControllerInstanceID, selection.Equals, []string{c.Config.InstanceID})
if err != nil {
- panic(err)
+ return nil, err
}
- return *instanceIDReq
+ c.logger.WithField("instance-id", instanceIDReq.String()).Infoln("instance id requirement")
+ return instanceIDReq, nil
}
-func (c *GatewayController) versionReq() labels.Requirement {
- versionReq, err := labels.NewRequirement(common.LabelArgoEventsGatewayVersion, selection.Equals, []string{v1alpha1.ArgoEventsGatewayVersion})
+// The controller informer adds new gateways to the controller's queue based on Add, Update, and Delete Event Handlers for the gateway Resources
+func (c *Controller) newGatewayInformer() (cache.SharedIndexInformer, error) {
+ labelSelector, err := c.instanceIDReq()
if err != nil {
- panic(err)
+ return nil, err
}
- return *versionReq
-}
-
-// The gateway-controller informer adds new Gateways to the gateway-controller-controller's queue based on Add, Update, and Delete Event Handlers for the Gateway Resources
-func (c *GatewayController) newGatewayInformer() cache.SharedIndexInformer {
- gatewayInformerFactory := gatewayinformers.NewFilteredSharedInformerFactory(
- c.gatewayClientset,
+ gatewayInformerFactory := gatewayinformers.NewSharedInformerFactoryWithOptions(
+ c.gatewayClient,
gatewayResyncPeriod,
- c.Config.Namespace,
- func(options *metav1.ListOptions) {
+ gatewayinformers.WithNamespace(c.Config.Namespace),
+ gatewayinformers.WithTweakListOptions(func(options *metav1.ListOptions) {
options.FieldSelector = fields.Everything().String()
- labelSelector := labels.NewSelector().Add(c.instanceIDReq(), c.versionReq())
options.LabelSelector = labelSelector.String()
- },
+ }),
)
informer := gatewayInformerFactory.Argoproj().V1alpha1().Gateways().Informer()
informer.AddEventHandler(
@@ -83,5 +76,5 @@ func (c *GatewayController) newGatewayInformer() cache.SharedIndexInformer {
},
},
)
- return informer
+ return informer, nil
}
diff --git a/controllers/gateway/informer_test.go b/controllers/gateway/informer_test.go
index bc9e58fe47..17f18e8706 100644
--- a/controllers/gateway/informer_test.go
+++ b/controllers/gateway/informer_test.go
@@ -17,29 +17,29 @@ limitations under the License.
package gateway
import (
+ "fmt"
"testing"
- "github.com/argoproj/argo-events/common"
- "github.com/smartystreets/goconvey/convey"
+ "github.com/argoproj/argo-events/pkg/apis/gateway/v1alpha1"
+ "github.com/stretchr/testify/assert"
"k8s.io/apimachinery/pkg/selection"
)
-func TestInformer(t *testing.T) {
- convey.Convey("Given a gateway controller", t, func() {
- controller := getGatewayController()
- convey.Convey("Instance ID required key must match", func() {
- req := controller.instanceIDReq()
- convey.So(req.Key(), convey.ShouldEqual, common.LabelKeyGatewayControllerInstanceID)
- convey.So(req.Operator(), convey.ShouldEqual, selection.Equals)
- convey.So(req.Values().Has("argo-events"), convey.ShouldBeTrue)
- })
- })
-
- convey.Convey("Given a gateway controller", t, func() {
- controller := getGatewayController()
- convey.Convey("Get a new gateway informer and make sure its not nil", func() {
- i := controller.newGatewayInformer()
- convey.So(i, convey.ShouldNotBeNil)
- })
- })
+func TestInformer_InstanceIDReq(t *testing.T) {
+ controller := newController()
+ req, err := controller.instanceIDReq()
+ assert.Nil(t, err)
+ assert.Equal(t, req.Key(), LabelControllerInstanceID)
+ assert.Equal(t, req.Operator(), selection.Equals)
+ assert.Equal(t, req.Values().Has("argo-events"), true)
+ assert.Equal(t, req.String(), fmt.Sprintf("%s=%s", LabelControllerInstanceID, "argo-events"))
+}
+
+func TestInformer_NewInformer(t *testing.T) {
+ controller := newController()
+ i, err := controller.newGatewayInformer()
+ assert.Nil(t, err)
+ assert.NotNil(t, i)
+ err = i.GetIndexer().Add(&v1alpha1.Gateway{})
+ assert.Nil(t, err)
}
diff --git a/controllers/gateway/operator.go b/controllers/gateway/operator.go
index da890be6f4..e4de8f960c 100644
--- a/controllers/gateway/operator.go
+++ b/controllers/gateway/operator.go
@@ -17,343 +17,207 @@ limitations under the License.
package gateway
import (
- "github.com/sirupsen/logrus"
"time"
- "github.com/pkg/errors"
-
"github.com/argoproj/argo-events/common"
"github.com/argoproj/argo-events/pkg/apis/gateway"
"github.com/argoproj/argo-events/pkg/apis/gateway/v1alpha1"
- corev1 "k8s.io/api/core/v1"
+ gwclient "github.com/argoproj/argo-events/pkg/client/gateway/clientset/versioned"
+ "github.com/sirupsen/logrus"
+ "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/apimachinery/pkg/util/wait"
)
-// the context of an operation on a gateway-controller.
-// the gateway-controller-controller creates this context each time it picks a Gateway off its queue.
-type gwOperationCtx struct {
- // gw is the gateway-controller object
- gw *v1alpha1.Gateway
- // updated indicates whether the gateway-controller object was updated and needs to be persisted back to k8
+// the context of an operation in the controller.
+// the controller creates this context each time it picks a Gateway off its queue.
+type gatewayContext struct {
+ // gateway is the controller object
+ gateway *v1alpha1.Gateway
+ // updated indicates whether the controller object was updated and needs to be persisted back to k8
updated bool
- // log is the logger for a gateway
- log *logrus.Logger
- // reference to the gateway-controller-controller
- controller *GatewayController
- // gwrctx is the context to handle child resource
- gwrctx gwResourceCtx
+ // logger is the logger for a gateway
+ logger *logrus.Logger
+ // reference to the controller
+ controller *Controller
}
-// newGatewayOperationCtx creates and initializes a new gOperationCtx object
-func newGatewayOperationCtx(gw *v1alpha1.Gateway, controller *GatewayController) *gwOperationCtx {
- gw = gw.DeepCopy()
- return &gwOperationCtx{
- gw: gw,
+// newGatewayContext creates and initializes a new gatewayContext object
+func newGatewayContext(gatewayObj *v1alpha1.Gateway, controller *Controller) *gatewayContext {
+ gatewayObj = gatewayObj.DeepCopy()
+ return &gatewayContext{
+ gateway: gatewayObj,
updated: false,
- log: common.NewArgoEventsLogger().WithFields(
+ logger: common.NewArgoEventsLogger().WithFields(
map[string]interface{}{
- common.LabelGatewayName: gw.Name,
- common.LabelNamespace: gw.Namespace,
+ common.LabelResourceName: gatewayObj.Name,
+ common.LabelNamespace: gatewayObj.Namespace,
}).Logger,
controller: controller,
- gwrctx: NewGatewayResourceContext(gw, controller),
}
}
// operate checks the status of gateway resource and takes action based on it.
-func (goc *gwOperationCtx) operate() error {
- defer func() {
- if goc.updated {
- var err error
- eventType := common.StateChangeEventType
- labels := map[string]string{
- common.LabelGatewayName: goc.gw.Name,
- common.LabelGatewayKeyPhase: string(goc.gw.Status.Phase),
- common.LabelKeyGatewayControllerInstanceID: goc.controller.Config.InstanceID,
- common.LabelOperation: "persist_gateway_state",
- }
- goc.gw, err = PersistUpdates(goc.controller.gatewayClientset, goc.gw, goc.log)
- if err != nil {
- goc.log.WithError(err).Error("failed to persist gateway update, escalating...")
- // escalate
- eventType = common.EscalationEventType
- }
+func (ctx *gatewayContext) operate() error {
+ defer ctx.updateGatewayState()
- labels[common.LabelEventType] = string(eventType)
- if err := common.GenerateK8sEvent(goc.controller.kubeClientset,
- "persist update",
- eventType,
- "gateway state update",
- goc.gw.Name,
- goc.gw.Namespace,
- goc.controller.Config.InstanceID,
- gateway.Kind,
- labels,
- ); err != nil {
- goc.log.WithError(err).Error("failed to create K8s event to log gateway state persist operation")
- return
- }
- goc.log.Info("successfully persisted gateway resource update and created K8s event")
- }
- goc.updated = false
- }()
+ ctx.logger.WithField(common.LabelPhase, string(ctx.gateway.Status.Phase)).Infoln("operating on the gateway...")
- goc.log.WithField(common.LabelPhase, string(goc.gw.Status.Phase)).Info("operating on the gateway")
+ if err := Validate(ctx.gateway); err != nil {
+ ctx.logger.WithError(err).Infoln("invalid gateway object")
+ return err
+ }
// check the state of a gateway and take actions accordingly
- switch goc.gw.Status.Phase {
+ switch ctx.gateway.Status.Phase {
case v1alpha1.NodePhaseNew:
- err := goc.createGatewayResources()
- if err != nil {
+ if err := ctx.createGatewayResources(); err != nil {
+ ctx.logger.WithError(err).Errorln("failed to create resources for the gateway")
+ ctx.markGatewayPhase(v1alpha1.NodePhaseError, err.Error())
return err
}
+ ctx.logger.Infoln("marking gateway as active")
+ ctx.markGatewayPhase(v1alpha1.NodePhaseRunning, "gateway is active")
- // Gateway is in error
- case v1alpha1.NodePhaseError:
- goc.log.Error("gateway is in error state. please check escalated K8 event for the error")
- err := goc.updateGatewayResources()
+ // Gateway is already running
+ case v1alpha1.NodePhaseRunning:
+ ctx.logger.Infoln("gateway is running")
+ err := ctx.updateGatewayResources()
if err != nil {
+ ctx.logger.WithError(err).Errorln("failed to update resources for the gateway")
+ ctx.markGatewayPhase(v1alpha1.NodePhaseError, err.Error())
return err
}
+ ctx.updated = true
- // Gateway is already running, do nothing
- case v1alpha1.NodePhaseRunning:
- goc.log.Info("gateway is running")
- err := goc.updateGatewayResources()
+ // Gateway is in error
+ case v1alpha1.NodePhaseError:
+ ctx.logger.Errorln("gateway is in error state. checking updates for gateway object...")
+ err := ctx.updateGatewayResources()
if err != nil {
+ ctx.logger.WithError(err).Errorln("failed to update resources for the gateway")
return err
}
+ ctx.markGatewayPhase(v1alpha1.NodePhaseRunning, "gateway is now active")
default:
- goc.log.WithField(common.LabelPhase, string(goc.gw.Status.Phase)).Panic("unknown gateway phase.")
+ ctx.logger.WithField(common.LabelPhase, string(ctx.gateway.Status.Phase)).Errorln("unknown gateway phase")
}
return nil
}
-func (goc *gwOperationCtx) createGatewayResources() error {
- err := Validate(goc.gw)
- if err != nil {
- goc.log.WithError(err).Error("gateway validation failed")
- err = errors.Wrap(err, "failed to validate gateway")
- goc.markGatewayPhase(v1alpha1.NodePhaseError, err.Error())
- return err
- }
- // Gateway pod has two components,
- // 1) Gateway Server - Listen events from event source and dispatches the event to gateway client
- // 2) Gateway Client - Listens for events from gateway server, convert them into cloudevents specification
- // compliant events and dispatch them to watchers.
- pod, err := goc.createGatewayPod()
- if err != nil {
- err = errors.Wrap(err, "failed to create gateway pod")
- goc.markGatewayPhase(v1alpha1.NodePhaseError, err.Error())
- return err
- }
- goc.log.WithField(common.LabelPodName, pod.Name).Info("gateway pod is created")
+// updateGatewayState updates the gateway state
+func (ctx *gatewayContext) updateGatewayState() {
+ if ctx.updated {
+ var err error
+ eventType := common.StateChangeEventType
+ labels := map[string]string{
+ common.LabelResourceName: ctx.gateway.Name,
+ LabelPhase: string(ctx.gateway.Status.Phase),
+ LabelControllerInstanceID: ctx.controller.Config.InstanceID,
+ common.LabelOperation: "persist_gateway_state",
+ }
- // expose gateway if service is configured
- if goc.gw.Spec.Service != nil {
- svc, err := goc.createGatewayService()
+ ctx.gateway, err = PersistUpdates(ctx.controller.gatewayClient, ctx.gateway, ctx.logger)
if err != nil {
- err = errors.Wrap(err, "failed to create gateway service")
- goc.markGatewayPhase(v1alpha1.NodePhaseError, err.Error())
- return err
+ ctx.logger.WithError(err).Errorln("failed to persist gateway update, escalating...")
+ eventType = common.EscalationEventType
}
- goc.log.WithField(common.LabelServiceName, svc.Name).Info("gateway service is created")
- }
- goc.log.Info("marking gateway as active")
- goc.markGatewayPhase(v1alpha1.NodePhaseRunning, "gateway is active")
- return nil
-}
-
-func (goc *gwOperationCtx) createGatewayPod() (*corev1.Pod, error) {
- pod, err := goc.gwrctx.newGatewayPod()
- if err != nil {
- goc.log.WithError(err).Error("failed to initialize pod for gateway")
- return nil, err
- }
- pod, err = goc.gwrctx.createGatewayPod(pod)
- if err != nil {
- goc.log.WithError(err).Error("failed to create pod for gateway")
- return nil, err
+ labels[common.LabelEventType] = string(eventType)
+ if err := common.GenerateK8sEvent(ctx.controller.k8sClient,
+ "persist update",
+ eventType,
+ "gateway state update",
+ ctx.gateway.Name,
+ ctx.gateway.Namespace,
+ ctx.controller.Config.InstanceID,
+ gateway.Kind,
+ labels,
+ ); err != nil {
+ ctx.logger.WithError(err).Errorln("failed to create K8s event to logger gateway state persist operation")
+ return
+ }
+ ctx.logger.Infoln("successfully persisted gateway resource update and created K8s event")
}
- return pod, nil
+ ctx.updated = false
}
-func (goc *gwOperationCtx) createGatewayService() (*corev1.Service, error) {
- svc, err := goc.gwrctx.newGatewayService()
- if err != nil {
- goc.log.WithError(err).Error("failed to initialize service for gateway")
- return nil, err
- }
- svc, err = goc.gwrctx.createGatewayService(svc)
- if err != nil {
- goc.log.WithError(err).Error("failed to create service for gateway")
- return nil, err
- }
- return svc, nil
-}
+// mark the gateway phase
+func (ctx *gatewayContext) markGatewayPhase(phase v1alpha1.NodePhase, message string) {
+ justCompleted := ctx.gateway.Status.Phase != phase
+ if justCompleted {
+ ctx.logger.WithFields(
+ map[string]interface{}{
+ "old": string(ctx.gateway.Status.Phase),
+ "new": string(phase),
+ },
+ ).Infoln("phase changed")
-func (goc *gwOperationCtx) updateGatewayResources() error {
- err := Validate(goc.gw)
- if err != nil {
- goc.log.WithError(err).Error("gateway validation failed")
- err = errors.Wrap(err, "failed to validate gateway")
- if goc.gw.Status.Phase != v1alpha1.NodePhaseError {
- goc.markGatewayPhase(v1alpha1.NodePhaseError, err.Error())
+ ctx.gateway.Status.Phase = phase
+ if ctx.gateway.ObjectMeta.Labels == nil {
+ ctx.gateway.ObjectMeta.Labels = make(map[string]string)
+ }
+ if ctx.gateway.Annotations == nil {
+ ctx.gateway.Annotations = make(map[string]string)
}
- return err
- }
- _, podChanged, err := goc.updateGatewayPod()
- if err != nil {
- err = errors.Wrap(err, "failed to update gateway pod")
- goc.markGatewayPhase(v1alpha1.NodePhaseError, err.Error())
- return err
+ ctx.gateway.ObjectMeta.Labels[LabelPhase] = string(phase)
+ // add annotations so a resource sensor can watch this gateway.
+ ctx.gateway.Annotations[LabelPhase] = string(phase)
}
- _, svcChanged, err := goc.updateGatewayService()
- if err != nil {
- err = errors.Wrap(err, "failed to update gateway service")
- goc.markGatewayPhase(v1alpha1.NodePhaseError, err.Error())
- return err
+ if ctx.gateway.Status.StartedAt.IsZero() {
+ ctx.gateway.Status.StartedAt = metav1.Time{Time: time.Now().UTC()}
}
- if goc.gw.Status.Phase != v1alpha1.NodePhaseRunning && (podChanged || svcChanged) {
- goc.markGatewayPhase(v1alpha1.NodePhaseRunning, "gateway is active")
- }
+ ctx.logger.WithFields(
+ map[string]interface{}{
+ "old": ctx.gateway.Status.Message,
+ "new": message,
+ },
+ ).Infoln("phase change message")
- return nil
+ ctx.gateway.Status.Message = message
+ ctx.updated = true
}
-func (goc *gwOperationCtx) updateGatewayPod() (*corev1.Pod, bool, error) {
- // Check if gateway spec has changed for pod.
- existingPod, err := goc.gwrctx.getGatewayPod()
- if err != nil {
- goc.log.WithError(err).Error("failed to get pod for gateway")
- return nil, false, err
- }
+// PersistUpdates of the gateway resource
+func PersistUpdates(client gwclient.Interface, gw *v1alpha1.Gateway, log *logrus.Logger) (*v1alpha1.Gateway, error) {
+ gatewayClient := client.ArgoprojV1alpha1().Gateways(gw.ObjectMeta.Namespace)
- // create a new pod spec
- newPod, err := goc.gwrctx.newGatewayPod()
- if err != nil {
- goc.log.WithError(err).Error("failed to initialize pod for gateway")
- return nil, false, err
- }
+ // in case persist update fails
+ oldgw := gw.DeepCopy()
- // check if pod spec remained unchanged
- if existingPod != nil {
- if existingPod.Annotations != nil && existingPod.Annotations[common.AnnotationGatewayResourceSpecHashName] == newPod.Annotations[common.AnnotationGatewayResourceSpecHashName] {
- goc.log.WithField(common.LabelPodName, existingPod.Name).Debug("gateway pod spec unchanged")
- return nil, false, nil
+ gw, err := gatewayClient.Update(gw)
+ if err != nil {
+ log.WithError(err).Warn("error updating gateway")
+ if errors.IsConflict(err) {
+ return oldgw, err
}
-
- // By now we are sure that the spec changed, so lets go ahead and delete the exisitng gateway pod.
- goc.log.WithField(common.LabelPodName, existingPod.Name).Info("gateway pod spec changed")
-
- err := goc.gwrctx.deleteGatewayPod(existingPod)
+ log.Info("re-applying updates on latest version and retrying update")
+ err = ReapplyUpdates(client, gw)
if err != nil {
- goc.log.WithError(err).Error("failed to delete pod for gateway")
- return nil, false, err
+ log.WithError(err).Error("failed to re-apply update")
+ return oldgw, err
}
-
- goc.log.WithField(common.LabelPodName, existingPod.Name).Info("gateway pod is deleted")
}
-
- // Create new pod for updated gateway spec.
- createdPod, err := goc.gwrctx.createGatewayPod(newPod)
- if err != nil {
- goc.log.WithError(err).Error("failed to create pod for gateway")
- return nil, false, err
- }
- goc.log.WithError(err).WithField(common.LabelPodName, newPod.Name).Info("gateway pod is created")
-
- return createdPod, true, nil
+ log.WithField(common.LabelPhase, string(gw.Status.Phase)).Info("gateway state updated successfully")
+ return gw, nil
}
-func (goc *gwOperationCtx) updateGatewayService() (*corev1.Service, bool, error) {
- // Check if gateway spec has changed for service.
- existingSvc, err := goc.gwrctx.getGatewayService()
- if err != nil {
- goc.log.WithError(err).Error("failed to get service for gateway")
- return nil, false, err
- }
-
- // create a new service spec
- newSvc, err := goc.gwrctx.newGatewayService()
- if err != nil {
- goc.log.WithError(err).Error("failed to initialize service for gateway")
- return nil, false, err
- }
-
- if existingSvc != nil {
- // updated spec doesn't have service defined, delete existing service.
- if newSvc == nil {
- if err := goc.gwrctx.deleteGatewayService(existingSvc); err != nil {
- return nil, false, err
+// ReapplyUpdates to gateway resource
+func ReapplyUpdates(client gwclient.Interface, gw *v1alpha1.Gateway) error {
+ return wait.ExponentialBackoff(common.DefaultRetry, func() (bool, error) {
+ gatewayClient := client.ArgoprojV1alpha1().Gateways(gw.Namespace)
+ g, err := gatewayClient.Update(gw)
+ if err != nil {
+ if !common.IsRetryableKubeAPIError(err) {
+ return false, err
}
- return nil, true, nil
+ return false, nil
}
-
- // check if service spec remained unchanged
- if existingSvc.Annotations[common.AnnotationGatewayResourceSpecHashName] == newSvc.Annotations[common.AnnotationGatewayResourceSpecHashName] {
- goc.log.WithField(common.LabelServiceName, existingSvc.Name).Debug("gateway service spec unchanged")
- return nil, false, nil
- }
-
- // service spec changed, delete existing service and create new one
- goc.log.WithField(common.LabelServiceName, existingSvc.Name).Info("gateway service spec changed")
-
- if err := goc.gwrctx.deleteGatewayService(existingSvc); err != nil {
- return nil, false, err
- }
- } else if newSvc == nil {
- // gateway service doesn't exist originally
- return nil, false, nil
- }
-
- // change createGatewayService to take a service spec
- createdSvc, err := goc.gwrctx.createGatewayService(newSvc)
- if err != nil {
- goc.log.WithError(err).Error("failed to create service for gateway")
- return nil, false, err
- }
- goc.log.WithField(common.LabelServiceName, createdSvc.Name).Info("gateway service is created")
-
- return createdSvc, true, nil
-}
-
-// mark the overall gateway phase
-func (goc *gwOperationCtx) markGatewayPhase(phase v1alpha1.NodePhase, message string) {
- justCompleted := goc.gw.Status.Phase != phase
- if justCompleted {
- goc.log.WithFields(
- map[string]interface{}{
- "old": string(goc.gw.Status.Phase),
- "new": string(phase),
- },
- ).Info("phase changed")
-
- goc.gw.Status.Phase = phase
- if goc.gw.ObjectMeta.Labels == nil {
- goc.gw.ObjectMeta.Labels = make(map[string]string)
- }
- if goc.gw.Annotations == nil {
- goc.gw.Annotations = make(map[string]string)
- }
- goc.gw.ObjectMeta.Labels[common.LabelGatewayKeyPhase] = string(phase)
- // add annotations so a resource sensor can watch this gateway.
- goc.gw.Annotations[common.LabelGatewayKeyPhase] = string(phase)
- }
- if goc.gw.Status.StartedAt.IsZero() {
- goc.gw.Status.StartedAt = metav1.Time{Time: time.Now().UTC()}
- }
- goc.log.WithFields(
- map[string]interface{}{
- "old": string(goc.gw.Status.Message),
- "new": message,
- },
- ).Info("message")
- goc.gw.Status.Message = message
- goc.updated = true
+ gw = g
+ return true, nil
+ })
}
diff --git a/controllers/gateway/operator_test.go b/controllers/gateway/operator_test.go
index 09528a9a54..419f30d5be 100644
--- a/controllers/gateway/operator_test.go
+++ b/controllers/gateway/operator_test.go
@@ -19,337 +19,147 @@ package gateway
import (
"testing"
+ "github.com/argoproj/argo-events/common"
"github.com/argoproj/argo-events/pkg/apis/gateway/v1alpha1"
- "github.com/ghodss/yaml"
- "github.com/smartystreets/goconvey/convey"
- corev1 "k8s.io/api/core/v1"
+ "github.com/stretchr/testify/assert"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
- "k8s.io/client-go/tools/cache"
)
-var testGatewayStr = `apiVersion: argoproj.io/v1alpha1
-kind: Gateway
-metadata:
- name: webhook-gateway
- namespace: argo-events
- labels:
- gateways.argoproj.io/gateway-controller-instanceid: argo-events
- gateway-name: "webhook-gateway"
- argo-events-gateway-version: v0.11
-spec:
- eventSource: "webhook-gateway-configmap"
- type: "webhook"
- processorPort: "9330"
- eventProtocol:
- type: "NATS"
- nats:
- url: "nats://nats.argo-events:4222"
- type: "Standard"
- eventVersion: "1.0"
- template:
- metadata:
- name: "webhook-gateway"
- labels:
- gateway-name: "webhook-gateway"
- spec:
- containers:
- - name: "gateway-client"
- image: "argoproj/gateway-client:v0.6.2"
- imagePullPolicy: "Always"
- command: ["/bin/gateway-client"]
- - name: "webhook-events"
- image: "argoproj/webhook-gateway:v0.6.2"
- imagePullPolicy: "Always"
- command: ["/bin/webhook-gateway"]
- serviceAccountName: "argo-events-sa"
- service:
- metadata:
- name: webhook-gateway-svc
- spec:
- selector:
- gateway-name: "webhook-gateway"
- ports:
- - port: 12000
- targetPort: 12000
- type: LoadBalancer`
-
-var (
- gatewayPodName = "webhook-gateway"
- gatewaySvcName = "webhook-gateway-svc"
-)
+func TestGatewayOperateLifecycle(t *testing.T) {
+ controller := newController()
+ ctx := newGatewayContext(gatewayObj.DeepCopy(), controller)
+ gateway, err := controller.gatewayClient.ArgoprojV1alpha1().Gateways(gatewayObj.Namespace).Create(gatewayObj)
+ assert.Nil(t, err)
+ assert.NotNil(t, gateway)
+
+ tests := []struct {
+ name string
+ updateStateFunc func()
+ testFunc func(oldMetadata *v1alpha1.GatewayResource)
+ }{
+ {
+ name: "process a new gateway object",
+ updateStateFunc: func() {},
+ testFunc: func(oldMetadata *v1alpha1.GatewayResource) {
+ assert.Nil(t, oldMetadata)
+ deployment, err := controller.k8sClient.AppsV1().Deployments(ctx.gateway.Status.Resources.Deployment.Namespace).Get(ctx.gateway.Status.Resources.Deployment.Name, metav1.GetOptions{})
+ assert.Nil(t, err)
+ assert.NotNil(t, deployment)
+ service, err := controller.k8sClient.CoreV1().Services(ctx.gateway.Status.Resources.Service.Namespace).Get(ctx.gateway.Status.Resources.Service.Name, metav1.GetOptions{})
+ assert.Nil(t, err)
+ assert.NotNil(t, service)
+ assert.NotNil(t, ctx.gateway.Status.Resources)
+ assert.Equal(t, ctx.gateway.Status.Resources.Deployment.Name, deployment.Name)
+ assert.Equal(t, ctx.gateway.Status.Resources.Service.Name, service.Name)
+ gateway, err := controller.gatewayClient.ArgoprojV1alpha1().Gateways(ctx.gateway.Namespace).Get(ctx.gateway.Name, metav1.GetOptions{})
+ assert.Nil(t, err)
+ assert.NotNil(t, gateway)
+ assert.Equal(t, gateway.Status.Phase, v1alpha1.NodePhaseRunning)
+ ctx.gateway = gateway.DeepCopy()
+ },
+ },
+ {
+ name: "process a updated gateway object",
+ updateStateFunc: func() {
+ ctx.gateway.Spec.Template.Spec.Containers[0].Name = "new-name"
+ },
+ testFunc: func(oldMetadata *v1alpha1.GatewayResource) {
+ currentMetadata := ctx.gateway.Status.Resources.DeepCopy()
+ assert.NotEqual(t, oldMetadata.Deployment.Annotations[common.AnnotationResourceSpecHash], currentMetadata.Deployment.Annotations[common.AnnotationResourceSpecHash])
+ assert.Equal(t, oldMetadata.Service.Annotations[common.AnnotationResourceSpecHash], currentMetadata.Service.Annotations[common.AnnotationResourceSpecHash])
+ },
+ },
+ {
+ name: "process a gateway object in error",
+ updateStateFunc: func() {
+ ctx.gateway.Status.Phase = v1alpha1.NodePhaseError
+ ctx.gateway.Spec.Template.Spec.Containers[0].Name = "fixed-name"
+ },
+ testFunc: func(oldMetadata *v1alpha1.GatewayResource) {
+ currentMetadata := ctx.gateway.Status.Resources.DeepCopy()
+ assert.NotEqual(t, oldMetadata.Deployment.Annotations[common.AnnotationResourceSpecHash], currentMetadata.Deployment.Annotations[common.AnnotationResourceSpecHash])
+ assert.Equal(t, oldMetadata.Service.Annotations[common.AnnotationResourceSpecHash], currentMetadata.Service.Annotations[common.AnnotationResourceSpecHash])
+ gateway, err := controller.gatewayClient.ArgoprojV1alpha1().Gateways(ctx.gateway.Namespace).Get(ctx.gateway.Name, metav1.GetOptions{})
+ assert.Nil(t, err)
+ assert.NotNil(t, gateway)
+ assert.Equal(t, gateway.Status.Phase, v1alpha1.NodePhaseRunning)
+ },
+ },
+ }
-func getGateway() (*v1alpha1.Gateway, error) {
- gwBytes := []byte(testGatewayStr)
- var gateway v1alpha1.Gateway
- err := yaml.Unmarshal(gwBytes, &gateway)
- return &gateway, err
+ for _, test := range tests {
+ t.Run(test.name, func(t *testing.T) {
+ oldMetadata := ctx.gateway.Status.Resources.DeepCopy()
+ test.updateStateFunc()
+ err := ctx.operate()
+ assert.Nil(t, err)
+ test.testFunc(oldMetadata)
+ })
+ }
}
-func waitForAllInformers(done chan struct{}, controller *GatewayController) {
- cache.WaitForCacheSync(done, controller.informer.HasSynced)
- cache.WaitForCacheSync(done, controller.podInformer.Informer().HasSynced)
- cache.WaitForCacheSync(done, controller.svcInformer.Informer().HasSynced)
+func TestPersistUpdates(t *testing.T) {
+ controller := newController()
+ ctx := newGatewayContext(gatewayObj.DeepCopy(), controller)
+ gateway, err := controller.gatewayClient.ArgoprojV1alpha1().Gateways(gatewayObj.Namespace).Create(gatewayObj)
+ assert.Nil(t, err)
+ assert.NotNil(t, gateway)
+
+ ctx.gateway = gateway
+ ctx.gateway.Spec.Template.Name = "updated-name"
+ gateway, err = PersistUpdates(controller.gatewayClient, ctx.gateway, controller.logger)
+ assert.Nil(t, err)
+ assert.Equal(t, gateway.Spec.Template.Name, "updated-name")
}
-func getPodAndService(controller *GatewayController, namespace string) (*corev1.Pod, *corev1.Service, error) {
- pod, err := controller.kubeClientset.CoreV1().Pods(namespace).Get(gatewayPodName, metav1.GetOptions{})
- if err != nil {
- return nil, nil, err
- }
- svc, err := controller.kubeClientset.CoreV1().Services(namespace).Get(gatewaySvcName, metav1.GetOptions{})
- if err != nil {
- return nil, nil, err
- }
- return pod, svc, err
+func TestReapplyUpdates(t *testing.T) {
+ controller := newController()
+ ctx := newGatewayContext(gatewayObj.DeepCopy(), controller)
+ gateway, err := controller.gatewayClient.ArgoprojV1alpha1().Gateways(gatewayObj.Namespace).Create(gatewayObj)
+ assert.Nil(t, err)
+ assert.NotNil(t, gateway)
+
+ ctx.gateway = gateway
+ ctx.gateway.Spec.Template.Name = "updated-name"
+ gateway, err = PersistUpdates(controller.gatewayClient, ctx.gateway, controller.logger)
+ assert.Nil(t, err)
+ assert.Equal(t, gateway.Spec.Template.Name, "updated-name")
}
-func deletePodAndService(controller *GatewayController, namespace string) error {
- err := controller.kubeClientset.CoreV1().Pods(namespace).Delete(gatewayPodName, &metav1.DeleteOptions{})
- if err != nil {
- return err
- }
- err = controller.kubeClientset.CoreV1().Services(namespace).Delete(gatewaySvcName, &metav1.DeleteOptions{})
- return err
+func TestOperator_MarkPhase(t *testing.T) {
+ controller := newController()
+ ctx := newGatewayContext(gatewayObj.DeepCopy(), controller)
+ assert.Equal(t, ctx.gateway.Status.Phase, v1alpha1.NodePhaseNew)
+ assert.Equal(t, ctx.gateway.Status.Message, "")
+ ctx.gateway.Status.Phase = v1alpha1.NodePhaseRunning
+ ctx.markGatewayPhase(v1alpha1.NodePhaseRunning, "node is active")
+ assert.Equal(t, ctx.gateway.Status.Phase, v1alpha1.NodePhaseRunning)
+ assert.Equal(t, ctx.gateway.Status.Message, "node is active")
}
-func TestGatewayOperateLifecycle(t *testing.T) {
- done := make(chan struct{})
- convey.Convey("Given a gateway resource spec, parse it", t, func() {
- fakeController := getGatewayController()
- gateway, err := getGateway()
- convey.Convey("Make sure no error occurs", func() {
- convey.So(err, convey.ShouldBeNil)
-
- convey.Convey("Create the gateway", func() {
- gateway, err = fakeController.gatewayClientset.ArgoprojV1alpha1().Gateways(fakeController.Config.Namespace).Create(gateway)
-
- convey.Convey("No error should occur and gateway resource should not be empty", func() {
- convey.So(err, convey.ShouldBeNil)
- convey.So(gateway, convey.ShouldNotBeNil)
-
- convey.Convey("Create a new gateway operation context", func() {
- goc := newGatewayOperationCtx(gateway, fakeController)
- convey.So(goc, convey.ShouldNotBeNil)
-
- convey.Convey("Operate on new gateway", func() {
- goc.markGatewayPhase(v1alpha1.NodePhaseNew, "test")
- err := goc.operate()
-
- convey.Convey("Operation must succeed", func() {
- convey.So(err, convey.ShouldBeNil)
- waitForAllInformers(done, fakeController)
-
- convey.Convey("A gateway pod and service must be created", func() {
- gatewayPod, gatewaySvc, err := getPodAndService(fakeController, gateway.Namespace)
- convey.So(err, convey.ShouldBeNil)
- convey.So(gatewayPod, convey.ShouldNotBeNil)
- convey.So(gatewaySvc, convey.ShouldNotBeNil)
-
- convey.Convey("Go to running state", func() {
- gateway, err := fakeController.gatewayClientset.ArgoprojV1alpha1().Gateways(gateway.Namespace).Get(gateway.Name, metav1.GetOptions{})
- convey.So(err, convey.ShouldBeNil)
- convey.So(gateway.Status.Phase, convey.ShouldEqual, v1alpha1.NodePhaseRunning)
- })
- })
- })
- })
-
- convey.Convey("Operate on gateway in running state", func() {
- err := fakeController.gatewayClientset.ArgoprojV1alpha1().Gateways(gateway.Namespace).Delete(gateway.Name, &metav1.DeleteOptions{})
- convey.So(err, convey.ShouldBeNil)
- gateway, err = fakeController.gatewayClientset.ArgoprojV1alpha1().Gateways(gateway.Namespace).Create(gateway)
- convey.So(err, convey.ShouldBeNil)
- convey.So(gateway, convey.ShouldNotBeNil)
-
- goc.markGatewayPhase(v1alpha1.NodePhaseNew, "test")
-
- // Operate it once to create pod and service
- waitForAllInformers(done, fakeController)
- err = goc.operate()
- convey.So(err, convey.ShouldBeNil)
- waitForAllInformers(done, fakeController)
-
- convey.Convey("Operation must succeed", func() {
- goc.markGatewayPhase(v1alpha1.NodePhaseRunning, "test")
-
- waitForAllInformers(done, fakeController)
- err := goc.operate()
- convey.So(err, convey.ShouldBeNil)
- waitForAllInformers(done, fakeController)
-
- convey.Convey("Untouch pod and service", func() {
- gatewayPod, gatewaySvc, err := getPodAndService(fakeController, gateway.Namespace)
- convey.So(err, convey.ShouldBeNil)
- convey.So(gatewayPod, convey.ShouldNotBeNil)
- convey.So(gatewaySvc, convey.ShouldNotBeNil)
-
- convey.Convey("Stay in running state", func() {
- gateway, err := fakeController.gatewayClientset.ArgoprojV1alpha1().Gateways(gateway.Namespace).Get(gateway.Name, metav1.GetOptions{})
- convey.So(err, convey.ShouldBeNil)
- convey.So(gateway.Status.Phase, convey.ShouldEqual, v1alpha1.NodePhaseRunning)
- })
- })
- })
-
- convey.Convey("Delete pod and service", func() {
- err := deletePodAndService(fakeController, gateway.Namespace)
- convey.So(err, convey.ShouldBeNil)
-
- convey.Convey("Operation must succeed", func() {
- goc.markGatewayPhase(v1alpha1.NodePhaseRunning, "test")
-
- waitForAllInformers(done, fakeController)
- err := goc.operate()
- convey.So(err, convey.ShouldBeNil)
- waitForAllInformers(done, fakeController)
-
- convey.Convey("Create pod and service", func() {
- gatewayPod, gatewaySvc, err := getPodAndService(fakeController, gateway.Namespace)
- convey.So(err, convey.ShouldBeNil)
- convey.So(gatewayPod, convey.ShouldNotBeNil)
- convey.So(gatewaySvc, convey.ShouldNotBeNil)
-
- convey.Convey("Stay in running state", func() {
- gateway, err := fakeController.gatewayClientset.ArgoprojV1alpha1().Gateways(gateway.Namespace).Get(gateway.Name, metav1.GetOptions{})
- convey.So(err, convey.ShouldBeNil)
- convey.So(gateway.Status.Phase, convey.ShouldEqual, v1alpha1.NodePhaseRunning)
- })
- })
- })
- })
-
- convey.Convey("Change pod and service spec", func() {
- goc.gwrctx.gw.Spec.Template.Spec.RestartPolicy = "Never"
- goc.gwrctx.gw.Spec.Service.Spec.ClusterIP = "127.0.0.1"
-
- gatewayPod, gatewaySvc, err := getPodAndService(fakeController, gateway.Namespace)
- convey.So(err, convey.ShouldBeNil)
- convey.So(gatewayPod, convey.ShouldNotBeNil)
- convey.So(gatewaySvc, convey.ShouldNotBeNil)
-
- convey.Convey("Operation must succeed", func() {
- goc.markGatewayPhase(v1alpha1.NodePhaseRunning, "test")
-
- waitForAllInformers(done, fakeController)
- err := goc.operate()
- convey.So(err, convey.ShouldBeNil)
- waitForAllInformers(done, fakeController)
-
- convey.Convey("Delete pod and service", func() {
- gatewayPod, gatewaySvc, err := getPodAndService(fakeController, gateway.Namespace)
- convey.So(err, convey.ShouldBeNil)
- convey.So(gatewayPod.Spec.RestartPolicy, convey.ShouldEqual, "Never")
- convey.So(gatewaySvc.Spec.ClusterIP, convey.ShouldEqual, "127.0.0.1")
-
- convey.Convey("Stay in running state", func() {
- gateway, err := fakeController.gatewayClientset.ArgoprojV1alpha1().Gateways(gateway.Namespace).Get(gateway.Name, metav1.GetOptions{})
- convey.So(err, convey.ShouldBeNil)
- convey.So(gateway.Status.Phase, convey.ShouldEqual, v1alpha1.NodePhaseRunning)
- })
- })
- })
- })
- })
-
- convey.Convey("Operate on gateway in error state", func() {
- err := fakeController.gatewayClientset.ArgoprojV1alpha1().Gateways(gateway.Namespace).Delete(gateway.Name, &metav1.DeleteOptions{})
- convey.So(err, convey.ShouldBeNil)
- gateway, err = fakeController.gatewayClientset.ArgoprojV1alpha1().Gateways(gateway.Namespace).Create(gateway)
- convey.So(err, convey.ShouldBeNil)
- convey.So(gateway, convey.ShouldNotBeNil)
-
- goc.markGatewayPhase(v1alpha1.NodePhaseNew, "test")
-
- // Operate it once to create pod and service
- waitForAllInformers(done, fakeController)
- err = goc.operate()
- convey.So(err, convey.ShouldBeNil)
- waitForAllInformers(done, fakeController)
-
- convey.Convey("Operation must succeed", func() {
- goc.markGatewayPhase(v1alpha1.NodePhaseError, "test")
-
- waitForAllInformers(done, fakeController)
- err := goc.operate()
- convey.So(err, convey.ShouldBeNil)
- waitForAllInformers(done, fakeController)
-
- convey.Convey("Untouch pod and service", func() {
- gatewayPod, gatewaySvc, err := getPodAndService(fakeController, gateway.Namespace)
- convey.So(err, convey.ShouldBeNil)
- convey.So(gatewayPod, convey.ShouldNotBeNil)
- convey.So(gatewaySvc, convey.ShouldNotBeNil)
-
- convey.Convey("Stay in error state", func() {
- gateway, err := fakeController.gatewayClientset.ArgoprojV1alpha1().Gateways(gateway.Namespace).Get(gateway.Name, metav1.GetOptions{})
- convey.So(err, convey.ShouldBeNil)
- convey.So(gateway.Status.Phase, convey.ShouldEqual, v1alpha1.NodePhaseError)
- })
- })
- })
-
- convey.Convey("Delete pod and service", func() {
- err := deletePodAndService(fakeController, gateway.Namespace)
- convey.So(err, convey.ShouldBeNil)
-
- convey.Convey("Operation must succeed", func() {
- goc.markGatewayPhase(v1alpha1.NodePhaseError, "test")
-
- waitForAllInformers(done, fakeController)
- err := goc.operate()
- convey.So(err, convey.ShouldBeNil)
- waitForAllInformers(done, fakeController)
-
- convey.Convey("Create pod and service", func() {
- gatewayPod, gatewaySvc, err := getPodAndService(fakeController, gateway.Namespace)
- convey.So(err, convey.ShouldBeNil)
- convey.So(gatewayPod, convey.ShouldNotBeNil)
- convey.So(gatewaySvc, convey.ShouldNotBeNil)
-
- convey.Convey("Go to running state", func() {
- gateway, err := fakeController.gatewayClientset.ArgoprojV1alpha1().Gateways(gateway.Namespace).Get(gateway.Name, metav1.GetOptions{})
- convey.So(err, convey.ShouldBeNil)
- convey.So(gateway.Status.Phase, convey.ShouldEqual, v1alpha1.NodePhaseRunning)
- })
- })
- })
- })
-
- convey.Convey("Change pod and service spec", func() {
- goc.gwrctx.gw.Spec.Template.Spec.RestartPolicy = "Never"
- goc.gwrctx.gw.Spec.Service.Spec.ClusterIP = "127.0.0.1"
-
- gatewayPod, gatewaySvc, err := getPodAndService(fakeController, gateway.Namespace)
- convey.So(err, convey.ShouldBeNil)
- convey.So(gatewayPod, convey.ShouldNotBeNil)
- convey.So(gatewaySvc, convey.ShouldNotBeNil)
-
- convey.Convey("Operation must succeed", func() {
- goc.markGatewayPhase(v1alpha1.NodePhaseError, "test")
-
- waitForAllInformers(done, fakeController)
- err := goc.operate()
- convey.So(err, convey.ShouldBeNil)
- waitForAllInformers(done, fakeController)
-
- convey.Convey("Delete pod and service", func() {
- gatewayPod, gatewaySvc, err := getPodAndService(fakeController, gateway.Namespace)
- convey.So(err, convey.ShouldBeNil)
- convey.So(gatewayPod.Spec.RestartPolicy, convey.ShouldEqual, "Never")
- convey.So(gatewaySvc.Spec.ClusterIP, convey.ShouldEqual, "127.0.0.1")
-
- convey.Convey("Go to running state", func() {
- gateway, err := fakeController.gatewayClientset.ArgoprojV1alpha1().Gateways(gateway.Namespace).Get(gateway.Name, metav1.GetOptions{})
- convey.So(err, convey.ShouldBeNil)
- convey.So(gateway.Status.Phase, convey.ShouldEqual, v1alpha1.NodePhaseRunning)
- })
- })
- })
- })
- })
- })
- })
- })
- })
- })
+func TestOperator_UpdateGatewayState(t *testing.T) {
+ controller := newController()
+ ctx := newGatewayContext(gatewayObj.DeepCopy(), controller)
+ gateway, err := controller.gatewayClient.ArgoprojV1alpha1().Gateways(gatewayObj.Namespace).Create(gatewayObj)
+ assert.Nil(t, err)
+ assert.NotNil(t, gateway)
+ ctx.gateway = gateway.DeepCopy()
+ assert.Equal(t, ctx.gateway.Status.Phase, v1alpha1.NodePhaseNew)
+ ctx.gateway.Status.Phase = v1alpha1.NodePhaseRunning
+ ctx.updated = true
+ ctx.updateGatewayState()
+ gateway, err = controller.gatewayClient.ArgoprojV1alpha1().Gateways(ctx.gateway.Namespace).Get(ctx.gateway.Name, metav1.GetOptions{})
+ assert.Nil(t, err)
+ assert.NotNil(t, gateway)
+ ctx.gateway = gateway.DeepCopy()
+ assert.Equal(t, ctx.gateway.Status.Phase, v1alpha1.NodePhaseRunning)
+
+ ctx.gateway.Status.Phase = v1alpha1.NodePhaseError
+ ctx.updated = false
+ ctx.updateGatewayState()
+ gateway, err = controller.gatewayClient.ArgoprojV1alpha1().Gateways(ctx.gateway.Namespace).Get(ctx.gateway.Name, metav1.GetOptions{})
+ assert.Nil(t, err)
+ assert.NotNil(t, gateway)
+ ctx.gateway = gateway.DeepCopy()
+ assert.Equal(t, ctx.gateway.Status.Phase, v1alpha1.NodePhaseRunning)
}
diff --git a/controllers/gateway/resource.go b/controllers/gateway/resource.go
index 4f59d794c2..6603ef9678 100644
--- a/controllers/gateway/resource.go
+++ b/controllers/gateway/resource.go
@@ -1,169 +1,248 @@
+/*
+Copyright 2018 BlackRock, Inc.
+
+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.
+*/
+
package gateway
import (
"github.com/argoproj/argo-events/common"
controllerscommon "github.com/argoproj/argo-events/controllers/common"
"github.com/argoproj/argo-events/pkg/apis/gateway/v1alpha1"
+ "github.com/pkg/errors"
+ appv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
+ apierr "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
- "k8s.io/apimachinery/pkg/labels"
- "k8s.io/apimachinery/pkg/selection"
)
-type gwResourceCtx struct {
- // gw is the gateway-controller object
- gw *v1alpha1.Gateway
- // reference to the gateway-controller-controller
- controller *GatewayController
-
- controllerscommon.ChildResourceContext
+// buildServiceResource builds a new service that exposes gateway.
+func (ctx *gatewayContext) buildServiceResource() (*corev1.Service, error) {
+ if ctx.gateway.Spec.Service == nil {
+ return nil, nil
+ }
+ service := ctx.gateway.Spec.Service.DeepCopy()
+ if err := controllerscommon.SetObjectMeta(ctx.gateway, service, v1alpha1.SchemaGroupVersionKind); err != nil {
+ return nil, err
+ }
+ return service, nil
}
-// NewGatewayResourceContext returns new gwResourceCtx
-func NewGatewayResourceContext(gw *v1alpha1.Gateway, controller *GatewayController) gwResourceCtx {
- return gwResourceCtx{
- gw: gw,
- controller: controller,
- ChildResourceContext: controllerscommon.ChildResourceContext{
- SchemaGroupVersionKind: v1alpha1.SchemaGroupVersionKind,
- LabelOwnerName: common.LabelGatewayName,
- LabelKeyOwnerControllerInstanceID: common.LabelKeyGatewayControllerInstanceID,
- AnnotationOwnerResourceHashName: common.AnnotationGatewayResourceSpecHashName,
- InstanceID: controller.Config.InstanceID,
+// buildDeploymentResource builds a deployment resource for the gateway
+func (ctx *gatewayContext) buildDeploymentResource() (*appv1.Deployment, error) {
+ if ctx.gateway.Spec.Template == nil {
+ return nil, errors.New("gateway template can't be empty")
+ }
+
+ podTemplate := ctx.gateway.Spec.Template.DeepCopy()
+
+ replica := int32(ctx.gateway.Spec.Replica)
+ if replica == 0 {
+ replica = 1
+ }
+
+ deployment := &appv1.Deployment{
+ ObjectMeta: podTemplate.ObjectMeta,
+ Spec: appv1.DeploymentSpec{
+ Replicas: &replica,
+ Template: *podTemplate,
},
}
+
+ if deployment.Spec.Template.Labels == nil {
+ deployment.Spec.Template.Labels = map[string]string{}
+ }
+ deployment.Spec.Template.Labels[common.LabelObjectName] = ctx.gateway.Name
+
+ if deployment.Spec.Selector == nil {
+ deployment.Spec.Selector = &metav1.LabelSelector{
+ MatchLabels: map[string]string{},
+ }
+ }
+ deployment.Spec.Selector.MatchLabels[common.LabelObjectName] = ctx.gateway.Name
+
+ envVars := []corev1.EnvVar{
+ {
+ Name: common.EnvVarNamespace,
+ Value: ctx.gateway.Namespace,
+ },
+ {
+ Name: common.EnvVarEventSource,
+ Value: ctx.gateway.Spec.EventSourceRef.Name,
+ },
+ {
+ Name: common.EnvVarResourceName,
+ Value: ctx.gateway.Name,
+ },
+ {
+ Name: common.EnvVarControllerInstanceID,
+ Value: ctx.controller.Config.InstanceID,
+ },
+ {
+ Name: common.EnvVarGatewayServerPort,
+ Value: ctx.gateway.Spec.ProcessorPort,
+ },
+ }
+
+ for i, container := range deployment.Spec.Template.Spec.Containers {
+ container.Env = append(container.Env, envVars...)
+ deployment.Spec.Template.Spec.Containers[i] = container
+ }
+
+ if err := controllerscommon.SetObjectMeta(ctx.gateway, deployment, v1alpha1.SchemaGroupVersionKind); err != nil {
+ return nil, errors.Wrap(err, "failed to set the object metadata on the deployment object")
+ }
+
+ return deployment, nil
}
-// gatewayResourceLabelSelector returns label selector of the gateway of the context
-func (grc *gwResourceCtx) gatewayResourceLabelSelector() (labels.Selector, error) {
- req, err := labels.NewRequirement(common.LabelGatewayName, selection.Equals, []string{grc.gw.Name})
+// createGatewayResources creates gateway deployment and service
+func (ctx *gatewayContext) createGatewayResources() error {
+ if ctx.gateway.Status.Resources == nil {
+ ctx.gateway.Status.Resources = &v1alpha1.GatewayResource{}
+ }
+
+ deployment, err := ctx.createGatewayDeployment()
if err != nil {
- return nil, err
+ return err
}
- return labels.NewSelector().Add(*req), nil
-}
+ ctx.gateway.Status.Resources.Deployment = &deployment.ObjectMeta
+ ctx.logger.WithField("name", deployment.Name).WithField("namespace", deployment.Namespace).Infoln("gateway deployment is created")
-// createGatewayService creates a given service
-func (grc *gwResourceCtx) createGatewayService(svc *corev1.Service) (*corev1.Service, error) {
- return grc.controller.kubeClientset.CoreV1().Services(grc.gw.Namespace).Create(svc)
-}
+ if ctx.gateway.Spec.Service != nil {
+ service, err := ctx.createGatewayService()
+ if err != nil {
+ return err
+ }
+ ctx.gateway.Status.Resources.Service = &service.ObjectMeta
+ ctx.logger.WithField("name", service.Name).WithField("namespace", service.Namespace).Infoln("gateway service is created")
+ }
-// deleteGatewayService deletes a given service
-func (grc *gwResourceCtx) deleteGatewayService(svc *corev1.Service) error {
- return grc.controller.kubeClientset.CoreV1().Services(grc.gw.Namespace).Delete(svc.Name, &metav1.DeleteOptions{})
+ return nil
}
-// getGatewayService returns the service of gateway
-func (grc *gwResourceCtx) getGatewayService() (*corev1.Service, error) {
- selector, err := grc.gatewayResourceLabelSelector()
+// createGatewayDeployment creates a deployment for the gateway
+func (ctx *gatewayContext) createGatewayDeployment() (*appv1.Deployment, error) {
+ deployment, err := ctx.buildDeploymentResource()
if err != nil {
return nil, err
}
- svcs, err := grc.controller.svcInformer.Lister().Services(grc.gw.Namespace).List(selector)
+ return ctx.controller.k8sClient.AppsV1().Deployments(deployment.Namespace).Create(deployment)
+}
+
+// createGatewayService creates a service for the gateway
+func (ctx *gatewayContext) createGatewayService() (*corev1.Service, error) {
+ svc, err := ctx.buildServiceResource()
if err != nil {
return nil, err
}
- if len(svcs) == 0 {
- return nil, nil
- }
- return svcs[0], nil
+ return ctx.controller.k8sClient.CoreV1().Services(svc.Namespace).Create(svc)
}
-// newGatewayService returns a new service that exposes gateway.
-func (grc *gwResourceCtx) newGatewayService() (*corev1.Service, error) {
- servicTemplateSpec := grc.gw.Spec.Service.DeepCopy()
- if servicTemplateSpec == nil {
- return nil, nil
+// updateGatewayResources updates gateway deployment and service
+func (ctx *gatewayContext) updateGatewayResources() error {
+ deployment, err := ctx.updateGatewayDeployment()
+ if err != nil {
+ return err
}
- service := &corev1.Service{
- ObjectMeta: servicTemplateSpec.ObjectMeta,
- Spec: servicTemplateSpec.Spec,
+ if deployment != nil {
+ ctx.gateway.Status.Resources.Deployment = &deployment.ObjectMeta
+ ctx.logger.WithField("name", deployment.Name).WithField("namespace", deployment.Namespace).Infoln("gateway deployment is updated")
}
- if service.Namespace == "" {
- service.Namespace = grc.gw.Namespace
+
+ service, err := ctx.updateGatewayService()
+ if err != nil {
+ return err
}
- if service.Name == "" {
- service.Name = common.DefaultServiceName(grc.gw.Name)
+ if service != nil {
+ ctx.gateway.Status.Resources.Service = &service.ObjectMeta
+ ctx.logger.WithField("name", service.Name).WithField("namespace", service.Namespace).Infoln("gateway service is updated")
+ return nil
}
- err := grc.SetObjectMeta(grc.gw, service)
- return service, err
+ ctx.gateway.Status.Resources.Service = nil
+ return nil
}
-// getGatewayPod returns the pod of gateway
-func (grc *gwResourceCtx) getGatewayPod() (*corev1.Pod, error) {
- selector, err := grc.gatewayResourceLabelSelector()
+// updateGatewayDeployment updates the gateway deployment
+func (ctx *gatewayContext) updateGatewayDeployment() (*appv1.Deployment, error) {
+ newDeployment, err := ctx.buildDeploymentResource()
if err != nil {
return nil, err
}
- pods, err := grc.controller.podInformer.Lister().Pods(grc.gw.Namespace).List(selector)
+
+ currentMetadata := ctx.gateway.Status.Resources.Deployment
+ if currentMetadata == nil {
+ return nil, errors.New("deployment metadata is expected to be set in gateway object")
+ }
+
+ currentDeployment, err := ctx.controller.k8sClient.AppsV1().Deployments(currentMetadata.Namespace).Get(currentMetadata.Name, metav1.GetOptions{})
if err != nil {
+ if apierr.IsNotFound(err) {
+ return ctx.controller.k8sClient.AppsV1().Deployments(newDeployment.Namespace).Create(newDeployment)
+ }
return nil, err
}
- if len(pods) == 0 {
- return nil, nil
+
+ if currentDeployment.Annotations != nil && currentDeployment.Annotations[common.AnnotationResourceSpecHash] != newDeployment.Annotations[common.AnnotationResourceSpecHash] {
+ if err := ctx.controller.k8sClient.AppsV1().Deployments(currentDeployment.Namespace).Delete(currentDeployment.Name, &metav1.DeleteOptions{}); err != nil {
+ return nil, err
+ }
+ return ctx.controller.k8sClient.AppsV1().Deployments(newDeployment.Namespace).Create(newDeployment)
}
- return pods[0], nil
-}
-// createGatewayPod creates a given pod
-func (grc *gwResourceCtx) createGatewayPod(pod *corev1.Pod) (*corev1.Pod, error) {
- return grc.controller.kubeClientset.CoreV1().Pods(grc.gw.Namespace).Create(pod)
+ return nil, nil
}
-// deleteGatewayPod deletes a given pod
-func (grc *gwResourceCtx) deleteGatewayPod(pod *corev1.Pod) error {
- return grc.controller.kubeClientset.CoreV1().Pods(grc.gw.Namespace).Delete(pod.Name, &metav1.DeleteOptions{})
-}
+// updateGatewayService updates the gateway service
+func (ctx *gatewayContext) updateGatewayService() (*corev1.Service, error) {
+ newService, err := ctx.buildServiceResource()
+ if err != nil {
+ return nil, err
+ }
+ if newService == nil && ctx.gateway.Status.Resources.Service != nil {
+ if err := ctx.controller.k8sClient.CoreV1().Services(ctx.gateway.Status.Resources.Service.Namespace).Delete(ctx.gateway.Status.Resources.Service.Name, &metav1.DeleteOptions{}); err != nil {
+ return nil, err
+ }
+ return nil, nil
+ }
-// newGatewayPod returns a new pod of gateway
-func (grc *gwResourceCtx) newGatewayPod() (*corev1.Pod, error) {
- podTemplateSpec := grc.gw.Spec.Template.DeepCopy()
- pod := &corev1.Pod{
- ObjectMeta: podTemplateSpec.ObjectMeta,
- Spec: podTemplateSpec.Spec,
+ if newService == nil {
+ return nil, nil
}
- if pod.Namespace == "" {
- pod.Namespace = grc.gw.Namespace
+
+ if ctx.gateway.Status.Resources.Service == nil {
+ return ctx.controller.k8sClient.CoreV1().Services(newService.Namespace).Create(newService)
}
- if pod.Name == "" {
- pod.Name = grc.gw.Name
+
+ currentMetadata := ctx.gateway.Status.Resources.Service
+ currentService, err := ctx.controller.k8sClient.CoreV1().Services(currentMetadata.Namespace).Get(currentMetadata.Name, metav1.GetOptions{})
+ if err != nil {
+ return ctx.controller.k8sClient.CoreV1().Services(newService.Namespace).Create(newService)
}
- grc.setupContainersForGatewayPod(pod)
- err := grc.SetObjectMeta(grc.gw, pod)
- return pod, err
-}
-// containers required for gateway deployment
-func (grc *gwResourceCtx) setupContainersForGatewayPod(pod *corev1.Pod) {
- // env variables
- envVars := []corev1.EnvVar{
- {
- Name: common.EnvVarGatewayNamespace,
- Value: grc.gw.Namespace,
- },
- {
- Name: common.EnvVarGatewayEventSourceConfigMap,
- Value: grc.gw.Spec.EventSource,
- },
- {
- Name: common.EnvVarGatewayName,
- Value: grc.gw.Name,
- },
- {
- Name: common.EnvVarGatewayControllerInstanceID,
- Value: grc.controller.Config.InstanceID,
- },
- {
- Name: common.EnvVarGatewayControllerName,
- Value: common.LabelGatewayControllerName,
- },
- {
- Name: common.EnvVarGatewayServerPort,
- Value: grc.gw.Spec.ProcessorPort,
- },
+ if currentMetadata == nil {
+ return nil, errors.New("service metadata is expected to be set in gateway object")
}
- for i, container := range pod.Spec.Containers {
- container.Env = append(container.Env, envVars...)
- pod.Spec.Containers[i] = container
+
+ if currentService.Annotations != nil && currentService.Annotations[common.AnnotationResourceSpecHash] != newService.Annotations[common.AnnotationResourceSpecHash] {
+ if err := ctx.controller.k8sClient.CoreV1().Services(currentMetadata.Namespace).Delete(currentMetadata.Name, &metav1.DeleteOptions{}); err != nil {
+ return nil, err
+ }
+ if ctx.gateway.Spec.Service != nil {
+ return ctx.controller.k8sClient.CoreV1().Services(newService.Namespace).Create(newService)
+ }
}
+
+ return currentService, nil
}
diff --git a/controllers/gateway/resource_test.go b/controllers/gateway/resource_test.go
new file mode 100644
index 0000000000..553d6af09a
--- /dev/null
+++ b/controllers/gateway/resource_test.go
@@ -0,0 +1,316 @@
+/*
+Copyright 2018 BlackRock, Inc.
+
+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.
+*/
+
+package gateway
+
+import (
+ "testing"
+
+ "github.com/argoproj/argo-events/common"
+ apicommon "github.com/argoproj/argo-events/pkg/apis/common"
+ "github.com/argoproj/argo-events/pkg/apis/gateway/v1alpha1"
+ "github.com/stretchr/testify/assert"
+ corev1 "k8s.io/api/core/v1"
+ apierror "k8s.io/apimachinery/pkg/api/errors"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/apimachinery/pkg/util/intstr"
+)
+
+var gatewayObj = &v1alpha1.Gateway{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "fake-gateway",
+ Namespace: common.DefaultControllerNamespace,
+ },
+ Spec: v1alpha1.GatewaySpec{
+ EventSourceRef: &v1alpha1.EventSourceRef{
+ Name: "fake-event-source",
+ },
+ Replica: 1,
+ Type: apicommon.WebhookEvent,
+ ProcessorPort: "8080",
+ Template: &corev1.PodTemplateSpec{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "webhook-gateway",
+ },
+ Spec: corev1.PodSpec{
+ Containers: []corev1.Container{
+ {
+ Name: "gateway-client",
+ Image: "argoproj/gateway-client",
+ ImagePullPolicy: corev1.PullAlways,
+ },
+ {
+ Name: "gateway-server",
+ ImagePullPolicy: corev1.PullAlways,
+ Image: "argoproj/webhook-gateway",
+ },
+ },
+ },
+ },
+ Service: &corev1.Service{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "webhook-gateway-svc",
+ },
+ Spec: corev1.ServiceSpec{
+ Type: corev1.ServiceTypeLoadBalancer,
+ Selector: map[string]string{
+ "gateway-name": "webhook-gateway",
+ },
+ Ports: []corev1.ServicePort{
+ {
+ Name: "server-port",
+ Port: 12000,
+ TargetPort: intstr.FromInt(12000),
+ },
+ },
+ },
+ },
+ EventProtocol: &apicommon.EventProtocol{
+ Type: apicommon.HTTP,
+ Http: apicommon.Http{
+ Port: "9330",
+ },
+ },
+ Watchers: &v1alpha1.NotificationWatchers{
+ Sensors: []v1alpha1.SensorNotificationWatcher{
+ {
+ Name: "fake-sensor",
+ Namespace: common.DefaultControllerNamespace,
+ },
+ },
+ },
+ },
+}
+
+func TestResource_BuildServiceResource(t *testing.T) {
+ controller := newController()
+ opCtx := newGatewayContext(gatewayObj, controller)
+ svc := opCtx.gateway.Spec.Service.DeepCopy()
+ opCtx.gateway.Spec.Service = nil
+
+ // If no service is defined
+ service, err := opCtx.buildServiceResource()
+ assert.Nil(t, err)
+ assert.Nil(t, service)
+ opCtx.gateway.Spec.Service = svc
+
+ // If service is defined
+ service, err = opCtx.buildServiceResource()
+ assert.Nil(t, err)
+ assert.NotNil(t, service)
+
+ opCtx.gateway.Spec.Service.Name = ""
+ opCtx.gateway.Spec.Service.Namespace = ""
+
+ service, err = opCtx.buildServiceResource()
+ assert.Nil(t, err)
+ assert.NotNil(t, service)
+ assert.Equal(t, service.Name, opCtx.gateway.Name)
+ assert.Equal(t, service.Namespace, opCtx.gateway.Namespace)
+
+ newSvc, err := controller.k8sClient.CoreV1().Services(service.Namespace).Create(service)
+ assert.Nil(t, err)
+ assert.NotNil(t, newSvc)
+ assert.Equal(t, newSvc.Name, opCtx.gateway.Name)
+ assert.Equal(t, len(newSvc.Spec.Ports), 1)
+ assert.Equal(t, newSvc.Spec.Type, corev1.ServiceTypeLoadBalancer)
+}
+
+func TestResource_BuildDeploymentResource(t *testing.T) {
+ controller := newController()
+ ctx := newGatewayContext(gatewayObj, controller)
+ deployment, err := ctx.buildDeploymentResource()
+ assert.Nil(t, err)
+ assert.NotNil(t, deployment)
+
+ for _, container := range deployment.Spec.Template.Spec.Containers {
+ assert.NotNil(t, container.Env)
+ assert.Equal(t, container.Env[0].Name, common.EnvVarNamespace)
+ assert.Equal(t, container.Env[0].Value, ctx.gateway.Namespace)
+ assert.Equal(t, container.Env[1].Name, common.EnvVarEventSource)
+ assert.Equal(t, container.Env[1].Value, ctx.gateway.Spec.EventSourceRef.Name)
+ assert.Equal(t, container.Env[2].Name, common.EnvVarResourceName)
+ assert.Equal(t, container.Env[2].Value, ctx.gateway.Name)
+ assert.Equal(t, container.Env[3].Name, common.EnvVarControllerInstanceID)
+ assert.Equal(t, container.Env[3].Value, ctx.controller.Config.InstanceID)
+ assert.Equal(t, container.Env[4].Name, common.EnvVarGatewayServerPort)
+ assert.Equal(t, container.Env[4].Value, ctx.gateway.Spec.ProcessorPort)
+ }
+
+ newDeployment, err := controller.k8sClient.AppsV1().Deployments(deployment.Namespace).Create(deployment)
+ assert.Nil(t, err)
+ assert.NotNil(t, newDeployment)
+ assert.Equal(t, newDeployment.Labels[common.LabelOwnerName], ctx.gateway.Name)
+ assert.NotNil(t, newDeployment.Annotations[common.AnnotationResourceSpecHash])
+}
+
+func TestResource_CreateGatewayResource(t *testing.T) {
+ tests := []struct {
+ name string
+ updateFunc func(ctx *gatewayContext)
+ testFunc func(controller *Controller, ctx *gatewayContext, t *testing.T)
+ }{
+ {
+ name: "gateway with deployment and service",
+ updateFunc: func(ctx *gatewayContext) {},
+ testFunc: func(controller *Controller, ctx *gatewayContext, t *testing.T) {
+ deploymentMetadata := ctx.gateway.Status.Resources.Deployment
+ serviceMetadata := ctx.gateway.Status.Resources.Service
+ deployment, err := controller.k8sClient.AppsV1().Deployments(deploymentMetadata.Namespace).Get(deploymentMetadata.Name, metav1.GetOptions{})
+ assert.Nil(t, err)
+ assert.NotNil(t, deployment)
+ service, err := controller.k8sClient.CoreV1().Services(serviceMetadata.Namespace).Get(serviceMetadata.Name, metav1.GetOptions{})
+ assert.Nil(t, err)
+ assert.NotNil(t, service)
+ },
+ },
+ {
+ name: "gateway with zero deployment replica",
+ updateFunc: func(ctx *gatewayContext) {
+ ctx.gateway.Spec.Replica = 0
+ },
+ testFunc: func(controller *Controller, ctx *gatewayContext, t *testing.T) {
+ deploymentMetadata := ctx.gateway.Status.Resources.Deployment
+ deployment, err := controller.k8sClient.AppsV1().Deployments(deploymentMetadata.Namespace).Get(deploymentMetadata.Name, metav1.GetOptions{})
+ assert.Nil(t, err)
+ assert.NotNil(t, deployment)
+ assert.Equal(t, *deployment.Spec.Replicas, int32(1))
+ },
+ },
+ {
+ name: "gateway with empty service template",
+ updateFunc: func(ctx *gatewayContext) {
+ ctx.gateway.Spec.Service = nil
+ },
+ testFunc: func(controller *Controller, ctx *gatewayContext, t *testing.T) {
+ deploymentMetadata := ctx.gateway.Status.Resources.Deployment
+ deployment, err := controller.k8sClient.AppsV1().Deployments(deploymentMetadata.Namespace).Get(deploymentMetadata.Name, metav1.GetOptions{})
+ assert.Nil(t, err)
+ assert.NotNil(t, deployment)
+ assert.Nil(t, ctx.gateway.Status.Resources.Service)
+ },
+ },
+ {
+ name: "gateway with resources in different namespaces",
+ updateFunc: func(ctx *gatewayContext) {
+ ctx.gateway.Spec.Template.Namespace = "new-namespace"
+ ctx.gateway.Spec.Service.Namespace = "new-namespace"
+ },
+ testFunc: func(controller *Controller, ctx *gatewayContext, t *testing.T) {
+ deploymentMetadata := ctx.gateway.Status.Resources.Deployment
+ serviceMetadata := ctx.gateway.Status.Resources.Service
+ deployment, err := controller.k8sClient.AppsV1().Deployments(deploymentMetadata.Namespace).Get(deploymentMetadata.Name, metav1.GetOptions{})
+ assert.Nil(t, err)
+ assert.NotNil(t, deployment)
+ service, err := controller.k8sClient.CoreV1().Services(serviceMetadata.Namespace).Get(serviceMetadata.Name, metav1.GetOptions{})
+ assert.Nil(t, err)
+ assert.NotNil(t, service)
+ assert.NotEqual(t, ctx.gateway.Namespace, deployment.Namespace)
+ assert.NotEqual(t, ctx.gateway.Namespace, service.Namespace)
+ },
+ },
+ {
+ name: "gateway with resources with empty names and namespaces",
+ updateFunc: func(ctx *gatewayContext) {
+ ctx.gateway.Spec.Template.Name = ""
+ ctx.gateway.Spec.Service.Name = ""
+ },
+ testFunc: func(controller *Controller, ctx *gatewayContext, t *testing.T) {
+ deploymentMetadata := ctx.gateway.Status.Resources.Deployment
+ serviceMetadata := ctx.gateway.Status.Resources.Service
+ deployment, err := controller.k8sClient.AppsV1().Deployments(deploymentMetadata.Namespace).Get(deploymentMetadata.Name, metav1.GetOptions{})
+ assert.Nil(t, err)
+ assert.NotNil(t, deployment)
+ service, err := controller.k8sClient.CoreV1().Services(serviceMetadata.Namespace).Get(serviceMetadata.Name, metav1.GetOptions{})
+ assert.Nil(t, err)
+ assert.NotNil(t, service)
+ assert.Equal(t, ctx.gateway.Name, deployment.Name)
+ assert.Equal(t, ctx.gateway.Name, service.Name)
+ },
+ },
+ }
+
+ for _, test := range tests {
+ t.Run(test.name, func(t *testing.T) {
+ controller := newController()
+ ctx := newGatewayContext(gatewayObj.DeepCopy(), controller)
+ test.updateFunc(ctx)
+ err := ctx.createGatewayResources()
+ assert.Nil(t, err)
+ test.testFunc(controller, ctx, t)
+ })
+ }
+}
+
+func TestResource_UpdateGatewayResource(t *testing.T) {
+ controller := newController()
+ ctx := newGatewayContext(gatewayObj.DeepCopy(), controller)
+ err := ctx.createGatewayResources()
+ assert.Nil(t, err)
+
+ tests := []struct {
+ name string
+ updateFunc func()
+ testFunc func(t *testing.T, oldMetadata *v1alpha1.GatewayResource)
+ }{
+ {
+ name: "update deployment resource on gateway template change",
+ updateFunc: func() {
+ ctx.gateway.Spec.Template.Spec.Containers[0].ImagePullPolicy = corev1.PullIfNotPresent
+ ctx.gateway.Spec.Service.Spec.Type = corev1.ServiceTypeNodePort
+ },
+ testFunc: func(t *testing.T, oldMetadata *v1alpha1.GatewayResource) {
+ currentMetadata := ctx.gateway.Status.Resources
+ deployment, err := controller.k8sClient.AppsV1().Deployments(currentMetadata.Deployment.Namespace).Get(currentMetadata.Deployment.Name, metav1.GetOptions{})
+ assert.Nil(t, err)
+ assert.NotNil(t, deployment)
+ assert.NotEqual(t, deployment.Annotations[common.AnnotationResourceSpecHash], oldMetadata.Deployment.Annotations[common.AnnotationResourceSpecHash])
+ service, err := controller.k8sClient.CoreV1().Services(currentMetadata.Service.Namespace).Get(currentMetadata.Service.Name, metav1.GetOptions{})
+ assert.Nil(t, err)
+ assert.NotNil(t, service)
+ assert.NotEqual(t, service.Annotations[common.AnnotationResourceSpecHash], oldMetadata.Service.Annotations[common.AnnotationResourceSpecHash])
+ },
+ },
+ {
+ name: "delete service resource if gateway service spec is removed",
+ updateFunc: func() {
+ ctx.gateway.Spec.Service = nil
+ },
+ testFunc: func(t *testing.T, oldMetadata *v1alpha1.GatewayResource) {
+ currentMetadata := ctx.gateway.Status.Resources
+ deployment, err := controller.k8sClient.AppsV1().Deployments(currentMetadata.Deployment.Namespace).Get(currentMetadata.Deployment.Name, metav1.GetOptions{})
+ assert.Nil(t, err)
+ assert.NotNil(t, deployment)
+ assert.Equal(t, deployment.Annotations[common.AnnotationResourceSpecHash], oldMetadata.Deployment.Annotations[common.AnnotationResourceSpecHash])
+ assert.Nil(t, ctx.gateway.Status.Resources.Service)
+ service, err := controller.k8sClient.CoreV1().Services(oldMetadata.Service.Namespace).Get(oldMetadata.Service.Name, metav1.GetOptions{})
+ assert.NotNil(t, err)
+ assert.Equal(t, apierror.IsNotFound(err), true)
+ assert.Nil(t, service)
+ },
+ },
+ }
+
+ for _, test := range tests {
+ t.Run(test.name, func(t *testing.T) {
+ metadata := ctx.gateway.Status.Resources.DeepCopy()
+ test.updateFunc()
+ err := ctx.updateGatewayResources()
+ assert.Nil(t, err)
+ test.testFunc(t, metadata)
+ })
+ }
+}
diff --git a/controllers/gateway/state.go b/controllers/gateway/state.go
deleted file mode 100644
index 7c61b47580..0000000000
--- a/controllers/gateway/state.go
+++ /dev/null
@@ -1,50 +0,0 @@
-package gateway
-
-import (
- "github.com/argoproj/argo-events/common"
- "github.com/argoproj/argo-events/pkg/apis/gateway/v1alpha1"
- gwclient "github.com/argoproj/argo-events/pkg/client/gateway/clientset/versioned"
- "github.com/sirupsen/logrus"
- "k8s.io/apimachinery/pkg/api/errors"
- "k8s.io/apimachinery/pkg/util/wait"
-)
-
-// PersistUpdates of the gateway resource
-func PersistUpdates(client gwclient.Interface, gw *v1alpha1.Gateway, log *logrus.Logger) (*v1alpha1.Gateway, error) {
- gatewayClient := client.ArgoprojV1alpha1().Gateways(gw.ObjectMeta.Namespace)
-
- // in case persist update fails
- oldgw := gw.DeepCopy()
-
- gw, err := gatewayClient.Update(gw)
- if err != nil {
- log.WithError(err).Warn("error updating gateway")
- if errors.IsConflict(err) {
- return oldgw, err
- }
- log.Info("re-applying updates on latest version and retrying update")
- err = ReapplyUpdates(client, gw)
- if err != nil {
- log.WithError(err).Error("failed to re-apply update")
- return oldgw, err
- }
- }
- log.WithField(common.LabelPhase, string(gw.Status.Phase)).Info("gateway state updated successfully")
- return gw, nil
-}
-
-// ReapplyUpdates to gateway resource
-func ReapplyUpdates(client gwclient.Interface, gw *v1alpha1.Gateway) error {
- return wait.ExponentialBackoff(common.DefaultRetry, func() (bool, error) {
- gatewayClient := client.ArgoprojV1alpha1().Gateways(gw.Namespace)
- g, err := gatewayClient.Update(gw)
- if err != nil {
- if !common.IsRetryableKubeAPIError(err) {
- return false, err
- }
- return false, nil
- }
- gw = g
- return true, nil
- })
-}
diff --git a/controllers/gateway/state_test.go b/controllers/gateway/state_test.go
deleted file mode 100644
index 9923e0b525..0000000000
--- a/controllers/gateway/state_test.go
+++ /dev/null
@@ -1,44 +0,0 @@
-package gateway
-
-import (
- "github.com/argoproj/argo-events/common"
- "testing"
-
- "github.com/argoproj/argo-events/pkg/client/gateway/clientset/versioned/fake"
- "github.com/smartystreets/goconvey/convey"
-)
-
-func TestPersistUpdates(t *testing.T) {
- convey.Convey("Given a gateway resource", t, func() {
- namespace := "argo-events"
- client := fake.NewSimpleClientset()
- logger := common.NewArgoEventsLogger()
- gw, err := getGateway()
- convey.So(err, convey.ShouldBeNil)
-
- convey.Convey("Create the gateway", func() {
- gw, err = client.ArgoprojV1alpha1().Gateways(namespace).Create(gw)
- convey.So(err, convey.ShouldBeNil)
- convey.So(gw, convey.ShouldNotBeNil)
-
- gw.ObjectMeta.Labels = map[string]string{
- "default": "default",
- }
-
- convey.Convey("Update the gateway", func() {
- updatedGw, err := PersistUpdates(client, gw, logger)
- convey.So(err, convey.ShouldBeNil)
- convey.So(updatedGw, convey.ShouldNotEqual, gw)
- convey.So(updatedGw.Labels, convey.ShouldResemble, gw.Labels)
-
- updatedGw.Labels["new"] = "new"
-
- convey.Convey("Reapply the gateway", func() {
- err := ReapplyUpdates(client, updatedGw)
- convey.So(err, convey.ShouldBeNil)
- convey.So(len(updatedGw.Labels), convey.ShouldEqual, 2)
- })
- })
- })
- })
-}
diff --git a/controllers/gateway/validate.go b/controllers/gateway/validate.go
index 73860ff8a2..b1dbb51a4c 100644
--- a/controllers/gateway/validate.go
+++ b/controllers/gateway/validate.go
@@ -17,50 +17,50 @@ limitations under the License.
package gateway
import (
- "fmt"
apicommon "github.com/argoproj/argo-events/pkg/apis/common"
"github.com/argoproj/argo-events/pkg/apis/gateway/v1alpha1"
+ "github.com/pkg/errors"
)
// Validate validates the gateway resource.
-// Exporting this function so that external APIs can use this to validate gateway resource.
-func Validate(gw *v1alpha1.Gateway) error {
- if gw.Spec.Template == nil {
- return fmt.Errorf("gateway pod template is not specified")
+func Validate(gatewayObj *v1alpha1.Gateway) error {
+ if gatewayObj.Spec.Template == nil {
+ return errors.New("gateway pod template is not specified")
}
- if gw.Spec.Type == "" {
- return fmt.Errorf("gateway type is not specified")
+ if gatewayObj.Spec.Type == "" {
+ return errors.New("gateway type is not specified")
}
- if gw.Spec.EventSource == "" {
- return fmt.Errorf("event source for the gateway is not specified")
+ if gatewayObj.Spec.EventSourceRef == nil {
+ return errors.New("event source for the gateway is not specified")
}
- if gw.Spec.ProcessorPort == "" {
- return fmt.Errorf("gateway processor port is not specified")
+
+ if gatewayObj.Spec.ProcessorPort == "" {
+ return errors.New("gateway processor port is not specified")
}
- switch gw.Spec.EventProtocol.Type {
+ switch gatewayObj.Spec.EventProtocol.Type {
case apicommon.HTTP:
- if gw.Spec.Watchers == nil || (gw.Spec.Watchers.Gateways == nil && gw.Spec.Watchers.Sensors == nil) {
- return fmt.Errorf("no associated watchers with gateway")
+ if gatewayObj.Spec.Watchers == nil || (gatewayObj.Spec.Watchers.Gateways == nil && gatewayObj.Spec.Watchers.Sensors == nil) {
+ return errors.New("no associated watchers with gateway")
}
- if gw.Spec.EventProtocol.Http.Port == "" {
- return fmt.Errorf("http server port is not defined")
+ if gatewayObj.Spec.EventProtocol.Http.Port == "" {
+ return errors.New("http server port is not defined")
}
case apicommon.NATS:
- if gw.Spec.EventProtocol.Nats.URL == "" {
- return fmt.Errorf("nats url is not defined")
+ if gatewayObj.Spec.EventProtocol.Nats.URL == "" {
+ return errors.New("nats url is not defined")
}
- if gw.Spec.EventProtocol.Nats.Type == "" {
- return fmt.Errorf("nats service type is not defined")
+ if gatewayObj.Spec.EventProtocol.Nats.Type == "" {
+ return errors.New("nats service type is not defined")
}
- if gw.Spec.EventProtocol.Nats.Type == apicommon.Streaming && gw.Spec.EventProtocol.Nats.ClientId == "" {
- return fmt.Errorf("client id must be specified when using nats streaming")
+ if gatewayObj.Spec.EventProtocol.Nats.Type == apicommon.Streaming && gatewayObj.Spec.EventProtocol.Nats.ClientId == "" {
+ return errors.New("client id must be specified when using nats streaming")
}
- if gw.Spec.EventProtocol.Nats.Type == apicommon.Streaming && gw.Spec.EventProtocol.Nats.ClusterId == "" {
- return fmt.Errorf("cluster id must be specified when using nats streaming")
+ if gatewayObj.Spec.EventProtocol.Nats.Type == apicommon.Streaming && gatewayObj.Spec.EventProtocol.Nats.ClusterId == "" {
+ return errors.New("cluster id must be specified when using nats streaming")
}
default:
- return fmt.Errorf("unknown gateway type")
+ return errors.New("unknown gateway type")
}
return nil
}
diff --git a/controllers/gateway/validate_test.go b/controllers/gateway/validate_test.go
index 47631c1dcb..a537b87d3e 100644
--- a/controllers/gateway/validate_test.go
+++ b/controllers/gateway/validate_test.go
@@ -18,28 +18,25 @@ package gateway
import (
"fmt"
- "github.com/argoproj/argo-events/pkg/apis/gateway/v1alpha1"
- "github.com/ghodss/yaml"
"io/ioutil"
"testing"
- "github.com/smartystreets/goconvey/convey"
+ "github.com/argoproj/argo-events/pkg/apis/gateway/v1alpha1"
+ "github.com/ghodss/yaml"
+ "github.com/stretchr/testify/assert"
)
func TestValidate(t *testing.T) {
dir := "../../examples/gateways"
- convey.Convey("Validate list of gateways", t, func() {
- files, err := ioutil.ReadDir(dir)
- convey.So(err, convey.ShouldBeNil)
- for _, file := range files {
- fmt.Println("filename: ", file.Name())
- content, err := ioutil.ReadFile(fmt.Sprintf("%s/%s", dir, file.Name()))
- convey.So(err, convey.ShouldBeNil)
- var gateway *v1alpha1.Gateway
- err = yaml.Unmarshal([]byte(content), &gateway)
- convey.So(err, convey.ShouldBeNil)
- err = Validate(gateway)
- convey.So(err, convey.ShouldBeNil)
- }
- })
+ files, err := ioutil.ReadDir(dir)
+ assert.Nil(t, err)
+ for _, file := range files {
+ content, err := ioutil.ReadFile(fmt.Sprintf("%s/%s", dir, file.Name()))
+ assert.Nil(t, err)
+ var gateway *v1alpha1.Gateway
+ err = yaml.Unmarshal([]byte(content), &gateway)
+ assert.Nil(t, err)
+ err = Validate(gateway)
+ assert.Nil(t, err)
+ }
}
diff --git a/cmd/controllers/sensor/main.go b/controllers/sensor/cmd/main.go
similarity index 78%
rename from cmd/controllers/sensor/main.go
rename to controllers/sensor/cmd/main.go
index 663897c9a1..aaaa9ac867 100644
--- a/cmd/controllers/sensor/main.go
+++ b/controllers/sensor/cmd/main.go
@@ -33,24 +33,24 @@ func main() {
}
// sensor-controller configuration
- configMap, ok := os.LookupEnv(common.EnvVarSensorControllerConfigMap)
+ configMap, ok := os.LookupEnv(common.EnvVarControllerConfigMap)
if !ok {
- configMap = common.DefaultConfigMapName(common.LabelSensorControllerName)
+ panic("controller configmap is not provided")
}
- namespace, ok := os.LookupEnv(common.SensorNamespace)
+ namespace, ok := os.LookupEnv(common.EnvVarNamespace)
if !ok {
namespace = common.DefaultControllerNamespace
}
// create a new sensor controller
- controller := sensor.NewSensorController(restConfig, configMap, namespace)
+ controller := sensor.NewController(restConfig, configMap, namespace)
// watch updates to sensor controller configuration
err = controller.ResyncConfig(namespace)
if err != nil {
panic(err)
}
- go controller.Run(context.Background(), 1, 1)
+ go controller.Run(context.Background(), 1)
select {}
}
diff --git a/gateways/community/slack/config_test.go b/controllers/sensor/common.go
similarity index 50%
rename from gateways/community/slack/config_test.go
rename to controllers/sensor/common.go
index b07e62fdd7..4867b4ef3c 100644
--- a/gateways/community/slack/config_test.go
+++ b/controllers/sensor/common.go
@@ -14,29 +14,16 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
-package slack
+package sensor
-import (
- "github.com/smartystreets/goconvey/convey"
- "testing"
-)
-
-var es = `
-hook:
- endpoint: "/"
- port: "8080"
- url: "testurl"
-token:
- name: fake-token
- key: fake
-`
+import "github.com/argoproj/argo-events/pkg/apis/sensor"
-func TestParseConfig(t *testing.T) {
- convey.Convey("Given a slack event source, parse it", t, func() {
- ps, err := parseEventSource(es)
- convey.So(err, convey.ShouldBeNil)
- convey.So(ps, convey.ShouldNotBeNil)
- _, ok := ps.(*slackEventSource)
- convey.So(ok, convey.ShouldEqual, true)
- })
-}
+// Labels
+const (
+ //LabelControllerInstanceID is the label which allows to separate application among multiple running controllers.
+ LabelControllerInstanceID = sensor.FullName + "/sensor-controller-instanceid"
+ // LabelPhase is a label applied to sensors to indicate the current phase of the sensor (for filtering purposes)
+ LabelPhase = sensor.FullName + "/phase"
+ // LabelComplete is the label to mark sensors as complete
+ LabelComplete = sensor.FullName + "/complete"
+)
diff --git a/controllers/sensor/config.go b/controllers/sensor/config.go
index 2515e6c838..d0f9f23732 100644
--- a/controllers/sensor/config.go
+++ b/controllers/sensor/config.go
@@ -19,9 +19,10 @@ package sensor
import (
"context"
"fmt"
- "github.com/argoproj/argo-events/common"
+ "github.com/argoproj/argo-events/common"
"github.com/ghodss/yaml"
+ "github.com/pkg/errors"
log "github.com/sirupsen/logrus"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -32,49 +33,49 @@ import (
)
// watchControllerConfigMap watches updates to sensor controller configmap
-func (c *SensorController) watchControllerConfigMap(ctx context.Context) (cache.Controller, error) {
- log.Info("watching sensor-controller config map updates")
- source := c.newControllerConfigMapWatch()
- _, controller := cache.NewInformer(
+func (controller *Controller) watchControllerConfigMap(ctx context.Context) (cache.Controller, error) {
+ log.Info("watching controller config map updates")
+ source := controller.newControllerConfigMapWatch()
+ _, ctrl := cache.NewInformer(
source,
&corev1.ConfigMap{},
0,
cache.ResourceEventHandlerFuncs{
AddFunc: func(obj interface{}) {
if cm, ok := obj.(*corev1.ConfigMap); ok {
- log.Info("detected EventSource update. updating the sensor-controller config.")
- err := c.updateConfig(cm)
+ log.Info("detected configuration update. updating the controller configuration")
+ err := controller.updateConfig(cm)
if err != nil {
- log.Errorf("update of config failed due to: %v", err)
+ log.Errorf("update of controller configuration failed due to: %v", err)
}
}
},
UpdateFunc: func(old, new interface{}) {
if newCm, ok := new.(*corev1.ConfigMap); ok {
- log.Info("detected EventSource update. updating the sensor-controller config.")
- err := c.updateConfig(newCm)
+ log.Info("detected configuration update. updating the controller configuration")
+ err := controller.updateConfig(newCm)
if err != nil {
- log.Errorf("update of config failed due to: %v", err)
+ log.Errorf("update of controller configuration failed due to: %v", err)
}
}
},
})
- go controller.Run(ctx.Done())
- return controller, nil
+ go ctrl.Run(ctx.Done())
+ return ctrl, nil
}
// newControllerConfigMapWatch returns a configmap watcher
-func (c *SensorController) newControllerConfigMapWatch() *cache.ListWatch {
- x := c.kubeClientset.CoreV1().RESTClient()
+func (controller *Controller) newControllerConfigMapWatch() *cache.ListWatch {
+ x := controller.k8sClient.CoreV1().RESTClient()
resource := "configmaps"
- name := c.ConfigMap
+ name := controller.ConfigMap
fieldSelector := fields.ParseSelectorOrDie(fmt.Sprintf("metadata.name=%s", name))
listFunc := func(options metav1.ListOptions) (runtime.Object, error) {
options.FieldSelector = fieldSelector.String()
req := x.Get().
- Namespace(c.Namespace).
+ Namespace(controller.Namespace).
Resource(resource).
VersionedParams(&options, metav1.ParameterCodec)
return req.Do().Get()
@@ -83,7 +84,7 @@ func (c *SensorController) newControllerConfigMapWatch() *cache.ListWatch {
options.Watch = true
options.FieldSelector = fieldSelector.String()
req := x.Get().
- Namespace(c.Namespace).
+ Namespace(controller.Namespace).
Resource(resource).
VersionedParams(&options, metav1.ParameterCodec)
return req.Watch()
@@ -91,26 +92,26 @@ func (c *SensorController) newControllerConfigMapWatch() *cache.ListWatch {
return &cache.ListWatch{ListFunc: listFunc, WatchFunc: watchFunc}
}
-// ResyncConfig reloads the sensor-controller config from the configmap
-func (c *SensorController) ResyncConfig(namespace string) error {
- cmClient := c.kubeClientset.CoreV1().ConfigMaps(namespace)
- cm, err := cmClient.Get(c.ConfigMap, metav1.GetOptions{})
+// ResyncConfig reloads the controller config from the configmap
+func (controller *Controller) ResyncConfig(namespace string) error {
+ cmClient := controller.k8sClient.CoreV1().ConfigMaps(namespace)
+ cm, err := cmClient.Get(controller.ConfigMap, metav1.GetOptions{})
if err != nil {
return err
}
- return c.updateConfig(cm)
+ return controller.updateConfig(cm)
}
-func (c *SensorController) updateConfig(cm *corev1.ConfigMap) error {
- configStr, ok := cm.Data[common.SensorControllerConfigMapKey]
+func (controller *Controller) updateConfig(cm *corev1.ConfigMap) error {
+ configStr, ok := cm.Data[common.ControllerConfigMapKey]
if !ok {
- return fmt.Errorf("configMap '%s' does not have key '%s'", c.ConfigMap, common.SensorControllerConfigMapKey)
+ return errors.Errorf("configMap '%s' does not have key '%s'", controller.ConfigMap, common.ControllerConfigMapKey)
}
- var config SensorControllerConfig
+ var config ControllerConfig
err := yaml.Unmarshal([]byte(configStr), &config)
if err != nil {
return err
}
- c.Config = config
+ controller.Config = config
return nil
}
diff --git a/controllers/sensor/config_test.go b/controllers/sensor/config_test.go
index 87ea54b68a..901c286015 100644
--- a/controllers/sensor/config_test.go
+++ b/controllers/sensor/config_test.go
@@ -20,52 +20,26 @@ import (
"testing"
"github.com/argoproj/argo-events/common"
- "github.com/smartystreets/goconvey/convey"
+ "github.com/stretchr/testify/assert"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
func TestSensorControllerConfigWatch(t *testing.T) {
- sc := getSensorController()
-
- convey.Convey("Given a sensor", t, func() {
- convey.Convey("Create a new watch and make sure watcher is not nil", func() {
- watcher := sc.newControllerConfigMapWatch()
- convey.So(watcher, convey.ShouldNotBeNil)
- })
- })
-
- convey.Convey("Given a sensor, resync config", t, func() {
- convey.Convey("Update a sensor configmap with new instance id and remove namespace", func() {
- cmObj := &corev1.ConfigMap{
- ObjectMeta: metav1.ObjectMeta{
- Namespace: common.DefaultControllerNamespace,
- Name: sc.ConfigMap,
- },
- Data: map[string]string{
- common.SensorControllerConfigMapKey: `instanceID: fake-instance-id`,
- },
- }
- cm, err := sc.kubeClientset.CoreV1().ConfigMaps(sc.Namespace).Create(cmObj)
- convey.Convey("Make sure no error occurs", func() {
- convey.So(err, convey.ShouldBeNil)
-
- convey.Convey("Updated sensor configmap must be non-nil", func() {
- convey.So(cm, convey.ShouldNotBeNil)
-
- convey.Convey("Resync the sensor configuration", func() {
- err := sc.ResyncConfig(cmObj.Namespace)
- convey.Convey("No error should occur while resyncing sensor configuration", func() {
- convey.So(err, convey.ShouldBeNil)
-
- convey.Convey("The updated instance id must be fake-instance-id", func() {
- convey.So(sc.Config.InstanceID, convey.ShouldEqual, "fake-instance-id")
- convey.So(sc.Config.Namespace, convey.ShouldBeEmpty)
- })
- })
- })
- })
- })
- })
- })
+ sensorController := getController()
+ configmap := &corev1.ConfigMap{
+ ObjectMeta: metav1.ObjectMeta{
+ Namespace: common.DefaultControllerNamespace,
+ Name: sensorController.ConfigMap,
+ },
+ Data: map[string]string{
+ common.ControllerConfigMapKey: `instanceID: fake-instance-id`,
+ },
+ }
+ cm, err := sensorController.k8sClient.CoreV1().ConfigMaps(sensorController.Namespace).Create(configmap)
+ assert.Nil(t, err)
+ assert.NotNil(t, cm)
+ err = sensorController.ResyncConfig(sensorController.Namespace)
+ assert.Nil(t, err)
+ assert.Equal(t, sensorController.Config.InstanceID, "fake-instance-id")
}
diff --git a/controllers/sensor/controller.go b/controllers/sensor/controller.go
index edf99cbfd1..b2923d670e 100644
--- a/controllers/sensor/controller.go
+++ b/controllers/sensor/controller.go
@@ -20,93 +20,85 @@ import (
"context"
"errors"
"fmt"
- "github.com/argoproj/argo-events/pkg/apis/sensor"
- "github.com/sirupsen/logrus"
"time"
- metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
- "k8s.io/apimachinery/pkg/labels"
+ base "github.com/argoproj/argo-events"
+ "github.com/argoproj/argo-events/common"
+ "github.com/argoproj/argo-events/pkg/apis/sensor"
+ "github.com/argoproj/argo-events/pkg/apis/sensor/v1alpha1"
+ clientset "github.com/argoproj/argo-events/pkg/client/sensor/clientset/versioned"
+ "github.com/sirupsen/logrus"
"k8s.io/apimachinery/pkg/util/wait"
- "k8s.io/client-go/informers"
- informersv1 "k8s.io/client-go/informers/core/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/cache"
"k8s.io/client-go/util/workqueue"
-
- base "github.com/argoproj/argo-events"
- "github.com/argoproj/argo-events/common"
- ccommon "github.com/argoproj/argo-events/controllers/common"
- "github.com/argoproj/argo-events/pkg/apis/sensor/v1alpha1"
- clientset "github.com/argoproj/argo-events/pkg/client/sensor/clientset/versioned"
)
// informer constants
const (
- sensorResyncPeriod = 20 * time.Minute
- sensorResourceResyncPeriod = 30 * time.Minute
- rateLimiterBaseDelay = 5 * time.Second
- rateLimiterMaxDelay = 1000 * time.Second
+ sensorResyncPeriod = 20 * time.Minute
+ rateLimiterBaseDelay = 5 * time.Second
+ rateLimiterMaxDelay = 1000 * time.Second
)
-// SensorControllerConfig contain the configuration settings for the sensor-controller
-type SensorControllerConfig struct {
- // InstanceID is a label selector to limit the sensor-controller's watch of sensor jobs to a specific instance.
- // If omitted, the sensor-controller watches sensors that *are not* labeled with an instance id.
+// ControllerConfig contain the configuration settings for the controller
+type ControllerConfig struct {
+ // InstanceID is a label selector to limit the controller'sensor watch of sensor jobs to a specific instance.
+ // If omitted, the controller watches sensors that *are not* labeled with an instance id.
InstanceID string
-
- // Namespace is a label selector filter to limit sensor-controller's watch to specific namespace
+ // Namespace is a label selector filter to limit controller'sensor watch to specific namespace
Namespace string
}
-// SensorController listens for new sensors and hands off handling of each sensor on the queue to the operator
-type SensorController struct {
- // EventSource is the name of the config map in which to derive configuration of the contoller
+// Controller listens for new sensors and hands off handling of each sensor on the queue to the operator
+type Controller struct {
+ // ConfigMap is the name of the config map in which to derive configuration of the controller
ConfigMap string
- // Namespace for sensor controller
+ // Namespace for controller
Namespace string
- // Config is the sensor-controller's configuration
- Config SensorControllerConfig
- // log is the logger for a gateway
- log *logrus.Logger
-
- // kubernetes config and apis
- kubeConfig *rest.Config
- kubeClientset kubernetes.Interface
- sensorClientset clientset.Interface
-
- // sensor informer and queue
- podInformer informersv1.PodInformer
- svcInformer informersv1.ServiceInformer
- informer cache.SharedIndexInformer
- queue workqueue.RateLimitingInterface
+ // Config is the controller'sensor configuration
+ Config ControllerConfig
+ // logger to logger stuff
+ logger *logrus.Logger
+ // kubeConfig is the rest K8s config
+ kubeConfig *rest.Config
+ // k8sClient is the Kubernetes client
+ k8sClient kubernetes.Interface
+ // sensorClient is the client for operations on the sensor custom resource
+ sensorClient clientset.Interface
+ // informer for sensor resource updates
+ informer cache.SharedIndexInformer
+ // queue to process watched sensor resources
+ queue workqueue.RateLimitingInterface
}
-// NewSensorController creates a new Controller
-func NewSensorController(rest *rest.Config, configMap, namespace string) *SensorController {
+// NewController creates a new Controller
+func NewController(rest *rest.Config, configMap, namespace string) *Controller {
rateLimiter := workqueue.NewItemExponentialFailureRateLimiter(rateLimiterBaseDelay, rateLimiterMaxDelay)
- return &SensorController{
- ConfigMap: configMap,
- Namespace: namespace,
- kubeConfig: rest,
- kubeClientset: kubernetes.NewForConfigOrDie(rest),
- sensorClientset: clientset.NewForConfigOrDie(rest),
- queue: workqueue.NewRateLimitingQueue(rateLimiter),
- log: common.NewArgoEventsLogger(),
+ return &Controller{
+ ConfigMap: configMap,
+ Namespace: namespace,
+ kubeConfig: rest,
+ k8sClient: kubernetes.NewForConfigOrDie(rest),
+ sensorClient: clientset.NewForConfigOrDie(rest),
+ queue: workqueue.NewRateLimitingQueue(rateLimiter),
+ logger: common.NewArgoEventsLogger(),
}
}
-func (c *SensorController) processNextItem() bool {
+// processNextItem processes the sensor resource object on the queue
+func (controller *Controller) processNextItem() bool {
// Wait until there is a new item in the queue
- key, quit := c.queue.Get()
+ key, quit := controller.queue.Get()
if quit {
return false
}
- defer c.queue.Done(key)
+ defer controller.queue.Done(key)
- obj, exists, err := c.informer.GetIndexer().GetByKey(key.(string))
+ obj, exists, err := controller.informer.GetIndexer().GetByKey(key.(string))
if err != nil {
- c.log.WithField(common.LabelSensorName, key.(string)).WithError(err).Warn("failed to get sensor from informer index")
+ controller.logger.WithField(common.LabelSensorName, key.(string)).WithError(err).Warnln("failed to get sensor from informer index")
return true
}
@@ -117,21 +109,21 @@ func (c *SensorController) processNextItem() bool {
s, ok := obj.(*v1alpha1.Sensor)
if !ok {
- c.log.WithField(common.LabelSensorName, key.(string)).WithError(err).Warn("key in index is not a sensor")
+ controller.logger.WithField(common.LabelSensorName, key.(string)).WithError(err).Warnln("key in index is not a sensor")
return true
}
- ctx := newSensorOperationCtx(s, c)
+ ctx := newSensorContext(s, controller)
err = ctx.operate()
if err != nil {
- if err := common.GenerateK8sEvent(c.kubeClientset,
+ if err := common.GenerateK8sEvent(controller.k8sClient,
fmt.Sprintf("failed to operate on sensor %s", s.Name),
common.EscalationEventType,
"sensor operation failed",
s.Name,
s.Namespace,
- c.Config.InstanceID,
+ controller.Config.InstanceID,
sensor.Kind,
map[string]string{
common.LabelSensorName: s.Name,
@@ -139,96 +131,73 @@ func (c *SensorController) processNextItem() bool {
common.LabelOperation: "controller_operation",
},
); err != nil {
- ctx.log.WithError(err).Error("failed to create K8s event to escalate sensor operation failure")
+ ctx.logger.WithError(err).Errorln("failed to create K8s event to escalate sensor operation failure")
}
}
- err = c.handleErr(err, key)
+ err = controller.handleErr(err, key)
if err != nil {
- ctx.log.WithError(err).Error("sensor controller is unable to handle the error")
+ ctx.logger.WithError(err).Errorln("controller is unable to handle the error")
}
return true
}
// handleErr checks if an error happened and make sure we will retry later
// returns an error if unable to handle the error
-func (c *SensorController) handleErr(err error, key interface{}) error {
+func (controller *Controller) handleErr(err error, key interface{}) error {
if err == nil {
// Forget about the #AddRateLimited history of key on every successful sync
// Ensure future updates for this key are not delayed because of outdated error history
- c.queue.Forget(key)
+ controller.queue.Forget(key)
return nil
}
// due to the base delay of 5ms of the DefaultControllerRateLimiter
- // requeues will happen very quickly even after a sensor pod goes down
- // we want to give the sensor pod a chance to come back up so we give a genorous number of retries
- if c.queue.NumRequeues(key) < 20 {
+ // re-queues will happen very quickly even after a sensor pod goes down
+ // we want to give the sensor pod a chance to come back up so we give a generous number of retries
+ if controller.queue.NumRequeues(key) < 20 {
// Re-enqueue the key rate limited. This key will be processed later again.
- c.queue.AddRateLimited(key)
+ controller.queue.AddRateLimited(key)
return nil
}
- return errors.New("exceeded max requeues")
+ return errors.New("exceeded max re-queues")
}
-// Run executes the sensor-controller
-func (c *SensorController) Run(ctx context.Context, ssThreads, eventThreads int) {
- defer c.queue.ShutDown()
+// Run executes the controller
+func (controller *Controller) Run(ctx context.Context, threads int) {
+ defer controller.queue.ShutDown()
- c.log.WithFields(
+ controller.logger.WithFields(
map[string]interface{}{
- common.LabelInstanceID: c.Config.InstanceID,
+ common.LabelInstanceID: controller.Config.InstanceID,
common.LabelVersion: base.GetVersion().Version,
- }).Info("starting sensor controller")
- _, err := c.watchControllerConfigMap(ctx)
- if err != nil {
- c.log.WithError(err).Error("failed to register watch for sensor controller config map")
- return
- }
+ }).Infoln("starting the controller...")
- c.informer = c.newSensorInformer()
- go c.informer.Run(ctx.Done())
-
- if !cache.WaitForCacheSync(ctx.Done(), c.informer.HasSynced) {
- c.log.Panic("timed out waiting for the caches to sync for sensors")
+ _, err := controller.watchControllerConfigMap(ctx)
+ if err != nil {
+ controller.logger.WithError(err).Error("failed to register watch for controller config map")
return
}
- listOptionsFunc := func(options *metav1.ListOptions) {
- labelSelector := labels.NewSelector().Add(c.instanceIDReq())
- options.LabelSelector = labelSelector.String()
- }
- factory := ccommon.ArgoEventInformerFactory{
- OwnerGroupVersionKind: v1alpha1.SchemaGroupVersionKind,
- OwnerInformer: c.informer,
- SharedInformerFactory: informers.NewFilteredSharedInformerFactory(c.kubeClientset, sensorResourceResyncPeriod, c.Config.Namespace, listOptionsFunc),
- Queue: c.queue,
- }
-
- c.podInformer = factory.NewPodInformer()
- go c.podInformer.Informer().Run(ctx.Done())
-
- if !cache.WaitForCacheSync(ctx.Done(), c.podInformer.Informer().HasSynced) {
- c.log.Panic("timed out waiting for the caches to sync for sensor pods")
- return
+ controller.informer, err = controller.newSensorInformer()
+ if err != nil {
+ controller.logger.WithError(err).Errorln("failed to create a new sensor controller")
}
+ go controller.informer.Run(ctx.Done())
- c.svcInformer = factory.NewServiceInformer()
- go c.svcInformer.Informer().Run(ctx.Done())
-
- if !cache.WaitForCacheSync(ctx.Done(), c.svcInformer.Informer().HasSynced) {
- c.log.Panic("timed out waiting for the caches to sync for sensor services")
+ if !cache.WaitForCacheSync(ctx.Done(), controller.informer.HasSynced) {
+ controller.logger.Panic("timed out waiting for the caches to sync for sensors")
return
}
- for i := 0; i < ssThreads; i++ {
- go wait.Until(c.runWorker, time.Second, ctx.Done())
+ for i := 0; i < threads; i++ {
+ go wait.Until(controller.runWorker, time.Second, ctx.Done())
}
<-ctx.Done()
}
-func (c *SensorController) runWorker() {
- for c.processNextItem() {
+func (controller *Controller) runWorker() {
+ for controller.processNextItem() {
}
}
diff --git a/controllers/sensor/controller_test.go b/controllers/sensor/controller_test.go
index 6130abcb8c..94b00b6a98 100644
--- a/controllers/sensor/controller_test.go
+++ b/controllers/sensor/controller_test.go
@@ -19,18 +19,13 @@ package sensor
import (
"fmt"
"testing"
- "time"
"github.com/argoproj/argo-events/common"
+ "github.com/argoproj/argo-events/pkg/apis/sensor/v1alpha1"
fakesensor "github.com/argoproj/argo-events/pkg/client/sensor/clientset/versioned/fake"
- "github.com/smartystreets/goconvey/convey"
- corev1 "k8s.io/api/core/v1"
+ "github.com/stretchr/testify/assert"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
- "k8s.io/apimachinery/pkg/runtime"
- "k8s.io/client-go/informers"
- "k8s.io/client-go/kubernetes"
"k8s.io/client-go/kubernetes/fake"
- "k8s.io/client-go/tools/cache"
"k8s.io/client-go/util/workqueue"
)
@@ -39,84 +34,55 @@ var (
SensorControllerInstanceID = "argo-events"
)
-func getFakePodSharedIndexInformer(clientset kubernetes.Interface) cache.SharedIndexInformer {
- // NewListWatchFromClient doesn't work with fake client.
- // ref: https://github.com/kubernetes/client-go/issues/352
- return cache.NewSharedIndexInformer(&cache.ListWatch{
- ListFunc: func(options metav1.ListOptions) (runtime.Object, error) {
- return clientset.CoreV1().Pods("").List(options)
- },
- WatchFunc: clientset.CoreV1().Pods("").Watch,
- }, &corev1.Pod{}, 1*time.Second, cache.Indexers{})
-}
-
-func getSensorController() *SensorController {
+func getController() *Controller {
clientset := fake.NewSimpleClientset()
- done := make(chan struct{})
- informer := getFakePodSharedIndexInformer(clientset)
- go informer.Run(done)
- factory := informers.NewSharedInformerFactory(clientset, 0)
- podInformer := factory.Core().V1().Pods()
- go podInformer.Informer().Run(done)
- svcInformer := factory.Core().V1().Services()
- go svcInformer.Informer().Run(done)
- return &SensorController{
+ controller := &Controller{
ConfigMap: SensorControllerConfigmap,
Namespace: common.DefaultControllerNamespace,
- Config: SensorControllerConfig{
+ Config: ControllerConfig{
Namespace: common.DefaultControllerNamespace,
InstanceID: SensorControllerInstanceID,
},
- kubeClientset: clientset,
- sensorClientset: fakesensor.NewSimpleClientset(),
- podInformer: podInformer,
- svcInformer: svcInformer,
- informer: informer,
- queue: workqueue.NewRateLimitingQueue(workqueue.DefaultControllerRateLimiter()),
- log: common.NewArgoEventsLogger(),
+ k8sClient: clientset,
+ sensorClient: fakesensor.NewSimpleClientset(),
+ queue: workqueue.NewRateLimitingQueue(workqueue.DefaultControllerRateLimiter()),
+ logger: common.NewArgoEventsLogger(),
}
+ informer, err := controller.newSensorInformer()
+ if err != nil {
+ panic(err)
+ }
+ controller.informer = informer
+ return controller
}
-func TestGatewayController(t *testing.T) {
- convey.Convey("Given a sensor controller, process queue items", t, func() {
- controller := getSensorController()
-
- convey.Convey("Create a resource queue, add new item and process it", func() {
- controller.queue = workqueue.NewRateLimitingQueue(workqueue.DefaultControllerRateLimiter())
- controller.informer = controller.newSensorInformer()
- controller.queue.Add("hi")
- res := controller.processNextItem()
-
- convey.Convey("Item from queue must be successfully processed", func() {
- convey.So(res, convey.ShouldBeTrue)
- })
-
- convey.Convey("Shutdown queue and make sure queue does not process next item", func() {
- controller.queue.ShutDown()
- res := controller.processNextItem()
- convey.So(res, convey.ShouldBeFalse)
- })
- })
+func TestController_ProcessNextItem(t *testing.T) {
+ controller := getController()
+ err := controller.informer.GetIndexer().Add(&v1alpha1.Sensor{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "fake-sensor",
+ Namespace: "fake-namespace",
+ },
+ Spec: v1alpha1.SensorSpec{},
})
+ assert.Nil(t, err)
+ controller.queue.Add("fake-sensor")
+ res := controller.processNextItem()
+ assert.Equal(t, res, true)
+ controller.queue.ShutDown()
+ res = controller.processNextItem()
+ assert.Equal(t, res, false)
+}
- convey.Convey("Given a sensor controller, handle errors in queue", t, func() {
- controller := getSensorController()
- convey.Convey("Create a resource queue and add an item", func() {
- controller.queue = workqueue.NewRateLimitingQueue(workqueue.DefaultControllerRateLimiter())
- controller.queue.Add("hi")
- convey.Convey("Handle an nil error", func() {
- err := controller.handleErr(nil, "hi")
- convey.So(err, convey.ShouldBeNil)
- })
- convey.Convey("Exceed max requeues", func() {
- controller.queue.Add("bye")
- var err error
- for i := 0; i < 21; i++ {
- err = controller.handleErr(fmt.Errorf("real error"), "bye")
- }
- convey.So(err, convey.ShouldNotBeNil)
- convey.So(err.Error(), convey.ShouldEqual, "exceeded max requeues")
- })
- })
- })
+func TestController_HandleErr(t *testing.T) {
+ controller := getController()
+ controller.queue.Add("hi")
+ err := controller.handleErr(nil, "hi")
+ assert.Nil(t, err)
+ controller.queue.Add("bye")
+ for i := 0; i < 21; i++ {
+ err = controller.handleErr(fmt.Errorf("real error"), "bye")
+ }
+ assert.NotNil(t, err)
+ assert.Equal(t, err.Error(), "exceeded max re-queues")
}
diff --git a/controllers/sensor/informer.go b/controllers/sensor/informer.go
index 7965c6bb74..8bcfbb31a7 100644
--- a/controllers/sensor/informer.go
+++ b/controllers/sensor/informer.go
@@ -17,49 +17,43 @@ limitations under the License.
package sensor
import (
- "github.com/argoproj/argo-events/common"
- "github.com/argoproj/argo-events/pkg/apis/sensor/v1alpha1"
+ sensorinformers "github.com/argoproj/argo-events/pkg/client/sensor/informers/externalversions"
+ "github.com/pkg/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/labels"
- "k8s.io/client-go/tools/cache"
-
- sensorinformers "github.com/argoproj/argo-events/pkg/client/sensor/informers/externalversions"
"k8s.io/apimachinery/pkg/selection"
+ "k8s.io/client-go/tools/cache"
)
-func (c *SensorController) instanceIDReq() labels.Requirement {
+func (controller *Controller) instanceIDReq() (*labels.Requirement, error) {
var instanceIDReq *labels.Requirement
var err error
- if c.Config.InstanceID == "" {
- panic("controller instance id must be specified")
+ if controller.Config.InstanceID == "" {
+ return nil, errors.New("controller instance id must be specified")
}
- instanceIDReq, err = labels.NewRequirement(common.LabelKeySensorControllerInstanceID, selection.Equals, []string{c.Config.InstanceID})
+ instanceIDReq, err = labels.NewRequirement(LabelControllerInstanceID, selection.Equals, []string{controller.Config.InstanceID})
if err != nil {
panic(err)
}
- return *instanceIDReq
+ return instanceIDReq, nil
}
-func (c *SensorController) versionReq() labels.Requirement {
- versionReq, err := labels.NewRequirement(common.LabelArgoEventsSensorVersion, selection.Equals, []string{v1alpha1.ArgoEventsSensorVersion})
+// The sensor informer adds new sensors to the controller'sensor queue based on Add, Update, and Delete event handlers for the sensor resources
+func (controller *Controller) newSensorInformer() (cache.SharedIndexInformer, error) {
+ labelSelector, err := controller.instanceIDReq()
if err != nil {
- panic(err)
+ return nil, err
}
- return *versionReq
-}
-// The sensor informer adds new Sensors to the sensor-controller's queue based on Add, Update, and Delete Event Handlers for the Sensor Resources
-func (c *SensorController) newSensorInformer() cache.SharedIndexInformer {
- sensorInformerFactory := sensorinformers.NewFilteredSharedInformerFactory(
- c.sensorClientset,
+ sensorInformerFactory := sensorinformers.NewSharedInformerFactoryWithOptions(
+ controller.sensorClient,
sensorResyncPeriod,
- c.Config.Namespace,
- func(options *metav1.ListOptions) {
+ sensorinformers.WithNamespace(controller.Config.Namespace),
+ sensorinformers.WithTweakListOptions(func(options *metav1.ListOptions) {
options.FieldSelector = fields.Everything().String()
- labelSelector := labels.NewSelector().Add(c.instanceIDReq(), c.versionReq())
options.LabelSelector = labelSelector.String()
- },
+ }),
)
informer := sensorInformerFactory.Argoproj().V1alpha1().Sensors().Informer()
informer.AddEventHandler(
@@ -67,22 +61,22 @@ func (c *SensorController) newSensorInformer() cache.SharedIndexInformer {
AddFunc: func(obj interface{}) {
key, err := cache.MetaNamespaceKeyFunc(obj)
if err == nil {
- c.queue.Add(key)
+ controller.queue.Add(key)
}
},
UpdateFunc: func(old, new interface{}) {
key, err := cache.MetaNamespaceKeyFunc(new)
if err == nil {
- c.queue.Add(key)
+ controller.queue.Add(key)
}
},
DeleteFunc: func(obj interface{}) {
key, err := cache.DeletionHandlingMetaNamespaceKeyFunc(obj)
if err == nil {
- c.queue.Add(key)
+ controller.queue.Add(key)
}
},
},
)
- return informer
+ return informer, nil
}
diff --git a/controllers/sensor/informer_test.go b/controllers/sensor/informer_test.go
index 75a5a8042e..738d603fe8 100644
--- a/controllers/sensor/informer_test.go
+++ b/controllers/sensor/informer_test.go
@@ -17,29 +17,17 @@ limitations under the License.
package sensor
import (
- "github.com/argoproj/argo-events/common"
"testing"
- "github.com/smartystreets/goconvey/convey"
+ "github.com/stretchr/testify/assert"
"k8s.io/apimachinery/pkg/selection"
)
-func TestInformer(t *testing.T) {
- convey.Convey("Given a sensor controller", t, func() {
- controller := getSensorController()
- convey.Convey("Instance ID required key must match", func() {
- req := controller.instanceIDReq()
- convey.So(req.Key(), convey.ShouldEqual, common.LabelKeySensorControllerInstanceID)
- convey.So(req.Operator(), convey.ShouldEqual, selection.Equals)
- convey.So(req.Values().Has("argo-events"), convey.ShouldBeTrue)
- })
- })
-
- convey.Convey("Given a sensor controller", t, func() {
- controller := getSensorController()
- convey.Convey("Get a new informer and make sure its not nil", func() {
- i := controller.newSensorInformer()
- convey.So(i, convey.ShouldNotBeNil)
- })
- })
+func TestInformer_InstanceIDReq(t *testing.T) {
+ controller := getController()
+ req, err := controller.instanceIDReq()
+ assert.Nil(t, err)
+ assert.Equal(t, req.Key(), LabelControllerInstanceID)
+ assert.Equal(t, req.Operator(), selection.Equals)
+ assert.Equal(t, req.Values().Has("argo-events"), true)
}
diff --git a/controllers/sensor/state.go b/controllers/sensor/node.go
similarity index 65%
rename from controllers/sensor/state.go
rename to controllers/sensor/node.go
index d66584742a..a7b14c1d97 100644
--- a/controllers/sensor/state.go
+++ b/controllers/sensor/node.go
@@ -17,16 +17,13 @@ limitations under the License.
package sensor
import (
- "github.com/argoproj/argo-events/pkg/apis/sensor/v1alpha1"
- "github.com/sirupsen/logrus"
"time"
"github.com/argoproj/argo-events/common"
apicommon "github.com/argoproj/argo-events/pkg/apis/common"
- sclient "github.com/argoproj/argo-events/pkg/client/sensor/clientset/versioned"
- "k8s.io/apimachinery/pkg/api/errors"
+ "github.com/argoproj/argo-events/pkg/apis/sensor/v1alpha1"
+ "github.com/sirupsen/logrus"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
- "k8s.io/apimachinery/pkg/util/wait"
)
// GetNodeByName returns a copy of the node from this sensor for the nodename
@@ -48,7 +45,7 @@ func InitializeNode(sensor *v1alpha1.Sensor, nodeName string, nodeType v1alpha1.
nodeID := sensor.NodeID(nodeName)
oldNode, ok := sensor.Status.Nodes[nodeID]
if ok {
- log.WithField(common.LabelNodeName, nodeName).Info("node already initialized")
+ log.WithField(common.LabelNodeName, nodeName).Infoln("node already initialized")
return &oldNode
}
node := v1alpha1.NodeStatus{
@@ -69,50 +66,10 @@ func InitializeNode(sensor *v1alpha1.Sensor, nodeName string, nodeType v1alpha1.
common.LabelNodeName: node.DisplayName,
"node-message": node.Message,
},
- ).Info("node is initialized")
+ ).Infoln("node is initialized")
return &node
}
-// PersistUpdates persists the updates to the Sensor resource
-func PersistUpdates(client sclient.Interface, sensor *v1alpha1.Sensor, controllerInstanceId string, log *logrus.Logger) (*v1alpha1.Sensor, error) {
- sensorClient := client.ArgoprojV1alpha1().Sensors(sensor.ObjectMeta.Namespace)
- // in case persist update fails
- oldsensor := sensor.DeepCopy()
-
- sensor, err := sensorClient.Update(sensor)
- if err != nil {
- if errors.IsConflict(err) {
- log.WithError(err).Error("error updating sensor")
- return oldsensor, err
- }
-
- log.Info("re-applying updates on latest version and retrying update")
- err = ReapplyUpdate(client, sensor)
- if err != nil {
- log.WithError(err).Error("failed to re-apply update")
- return oldsensor, err
- }
- }
- log.WithField(common.LabelPhase, string(sensor.Status.Phase)).Info("sensor state updated successfully")
- return sensor, nil
-}
-
-// Reapply the update to sensor
-func ReapplyUpdate(sensorClient sclient.Interface, sensor *v1alpha1.Sensor) error {
- return wait.ExponentialBackoff(common.DefaultRetry, func() (bool, error) {
- client := sensorClient.ArgoprojV1alpha1().Sensors(sensor.Namespace)
- s, err := client.Update(sensor)
- if err != nil {
- if !common.IsRetryableKubeAPIError(err) {
- return false, err
- }
- return false, nil
- }
- sensor = s
- return true, nil
- })
-}
-
// MarkNodePhase marks the node with a phase, returns the node
func MarkNodePhase(sensor *v1alpha1.Sensor, nodeName string, nodeType v1alpha1.NodeType, phase v1alpha1.NodePhase, event *apicommon.Event, log *logrus.Logger, message ...string) *v1alpha1.NodeStatus {
node := GetNodeByName(sensor, nodeName)
@@ -123,7 +80,7 @@ func MarkNodePhase(sensor *v1alpha1.Sensor, nodeName string, nodeType v1alpha1.N
common.LabelNodeName: node.Name,
common.LabelPhase: string(node.Phase),
},
- ).Info("marking node phase")
+ ).Infoln("marking node phase")
node.Phase = phase
}
diff --git a/controllers/sensor/node_test.go b/controllers/sensor/node_test.go
new file mode 100644
index 0000000000..e66e16f9dd
--- /dev/null
+++ b/controllers/sensor/node_test.go
@@ -0,0 +1,93 @@
+/*
+Copyright 2018 BlackRock, Inc.
+
+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.
+*/
+
+package sensor
+
+import (
+ "testing"
+
+ "github.com/argoproj/argo-events/common"
+ apicommon "github.com/argoproj/argo-events/pkg/apis/common"
+ "github.com/argoproj/argo-events/pkg/apis/sensor/v1alpha1"
+ fakesensor "github.com/argoproj/argo-events/pkg/client/sensor/clientset/versioned/fake"
+ "github.com/stretchr/testify/assert"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+)
+
+func TestSensorState(t *testing.T) {
+ fakeSensorClient := fakesensor.NewSimpleClientset()
+ logger := common.NewArgoEventsLogger()
+ fakeSensor := &v1alpha1.Sensor{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "test-sensor",
+ Namespace: "test",
+ },
+ }
+
+ fakeSensor, err := fakeSensorClient.ArgoprojV1alpha1().Sensors(fakeSensor.Namespace).Create(fakeSensor)
+ assert.Nil(t, err)
+
+ tests := []struct {
+ name string
+ testFunc func(t *testing.T)
+ }{
+ {
+ name: "initialize a new node",
+ testFunc: func(t *testing.T) {
+ status := InitializeNode(fakeSensor, "first_node", v1alpha1.NodeTypeEventDependency, logger)
+ assert.Equal(t, status.Phase, v1alpha1.NodePhaseNew)
+ },
+ },
+ {
+ name: "persist updates to the sensor",
+ testFunc: func(t *testing.T) {
+ sensor, err := PersistUpdates(fakeSensorClient, fakeSensor, logger)
+ assert.Nil(t, err)
+ assert.Equal(t, len(sensor.Status.Nodes), 1)
+ },
+ },
+ {
+ name: "mark node state to active",
+ testFunc: func(t *testing.T) {
+ status := MarkNodePhase(fakeSensor, "first_node", v1alpha1.NodeTypeEventDependency, v1alpha1.NodePhaseActive, &apicommon.Event{
+ Payload: []byte("test payload"),
+ }, logger)
+ assert.Equal(t, status.Phase, v1alpha1.NodePhaseActive)
+ },
+ },
+ {
+ name: "reapply the update",
+ testFunc: func(t *testing.T) {
+ err := ReapplyUpdate(fakeSensorClient, fakeSensor)
+ assert.Nil(t, err)
+ },
+ },
+ {
+ name: "fetch sensor and check updates are applied",
+ testFunc: func(t *testing.T) {
+ updatedSensor, err := fakeSensorClient.ArgoprojV1alpha1().Sensors(fakeSensor.Namespace).Get(fakeSensor.Name, metav1.GetOptions{})
+ assert.Nil(t, err)
+ assert.Equal(t, len(updatedSensor.Status.Nodes), 1)
+ },
+ },
+ }
+
+ for _, test := range tests {
+ t.Run(test.name, func(t *testing.T) {
+ test.testFunc(t)
+ })
+ }
+}
diff --git a/controllers/sensor/operator.go b/controllers/sensor/operator.go
index 99c058cd8c..95227ac995 100644
--- a/controllers/sensor/operator.go
+++ b/controllers/sensor/operator.go
@@ -17,388 +17,327 @@ limitations under the License.
package sensor
import (
- "github.com/sirupsen/logrus"
"time"
- "github.com/pkg/errors"
-
"github.com/argoproj/argo-events/common"
+ apicommon "github.com/argoproj/argo-events/pkg/apis/common"
"github.com/argoproj/argo-events/pkg/apis/sensor"
"github.com/argoproj/argo-events/pkg/apis/sensor/v1alpha1"
- corev1 "k8s.io/api/core/v1"
+ sensorclientset "github.com/argoproj/argo-events/pkg/client/sensor/clientset/versioned"
+ "github.com/sirupsen/logrus"
+ "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/apimachinery/pkg/util/wait"
)
// the context of an operation on a sensor.
-// the sensor-controller creates this context each time it picks a Sensor off its queue.
-type sOperationCtx struct {
- // s is the sensor object
- s *v1alpha1.Sensor
+// the controller creates this context each time it picks a Sensor off its queue.
+type sensorContext struct {
+ // sensor is the sensor object
+ sensor *v1alpha1.Sensor
// updated indicates whether the sensor object was updated and needs to be persisted back to k8
updated bool
- // log is the logrus logging context to correlate logs with a sensor
- log *logrus.Logger
- // reference to the sensor-controller
- controller *SensorController
- // srctx is the context to handle child resource
- srctx sResourceCtx
+ // logger logs stuff
+ logger *logrus.Logger
+ // reference to the controller
+ controller *Controller
}
-// newSensorOperationCtx creates and initializes a new sOperationCtx object
-func newSensorOperationCtx(s *v1alpha1.Sensor, controller *SensorController) *sOperationCtx {
- return &sOperationCtx{
- s: s.DeepCopy(),
+// newSensorContext creates and initializes a new sensorContext object
+func newSensorContext(sensorObj *v1alpha1.Sensor, controller *Controller) *sensorContext {
+ return &sensorContext{
+ sensor: sensorObj.DeepCopy(),
updated: false,
- log: common.NewArgoEventsLogger().WithFields(
+ logger: common.NewArgoEventsLogger().WithFields(
map[string]interface{}{
- common.LabelSensorName: s.Name,
- common.LabelNamespace: s.Namespace,
+ common.LabelSensorName: sensorObj.Name,
+ common.LabelNamespace: sensorObj.Namespace,
}).Logger,
controller: controller,
- srctx: NewSensorResourceContext(s, controller),
}
}
-// operate on sensor resource
-func (soc *sOperationCtx) operate() error {
- defer func() {
- if soc.updated {
- // persist updates to sensor resource
- labels := map[string]string{
- common.LabelSensorName: soc.s.Name,
- common.LabelSensorKeyPhase: string(soc.s.Status.Phase),
- common.LabelKeySensorControllerInstanceID: soc.controller.Config.InstanceID,
- common.LabelOperation: "persist_state_update",
- }
- eventType := common.StateChangeEventType
+// operate manages the lifecycle of a sensor object
+func (ctx *sensorContext) operate() error {
+ defer ctx.updateSensorState()
- updatedSensor, err := PersistUpdates(soc.controller.sensorClientset, soc.s, soc.controller.Config.InstanceID, soc.log)
- if err != nil {
- soc.log.WithError(err).Error("failed to persist sensor update, escalating...")
+ ctx.logger.Infoln("processing the sensor")
- // escalate failure
- eventType = common.EscalationEventType
- }
-
- // update sensor ref. in case of failure to persist updates, this is a deep copy of old sensor resource
- soc.s = updatedSensor
-
- labels[common.LabelEventType] = string(eventType)
- if err := common.GenerateK8sEvent(soc.controller.kubeClientset,
- "persist update",
- eventType,
- "sensor state update",
- soc.s.Name,
- soc.s.Namespace,
- soc.controller.Config.InstanceID,
- sensor.Kind,
- labels); err != nil {
- soc.log.WithError(err).Error("failed to create K8s event to log sensor state persist operation")
- return
- }
- soc.log.Info("successfully persisted sensor resource update and created K8s event")
- }
- soc.updated = false
- }()
+ // Validation failure prevents any sort processing of the sensor object
+ if err := ValidateSensor(ctx.sensor); err != nil {
+ ctx.logger.WithError(err).Errorln("failed to validate sensor")
+ ctx.markSensorPhase(v1alpha1.NodePhaseError, false, err.Error())
+ return err
+ }
- switch soc.s.Status.Phase {
+ switch ctx.sensor.Status.Phase {
case v1alpha1.NodePhaseNew:
- err := soc.createSensorResources()
- if err != nil {
- return err
+ // If the sensor phase is new
+ // 1. Initialize all nodes - dependencies, dependency groups and triggers
+ // 2. Make dependencies and dependency groups as active
+ // 3. Create a deployment and service (if needed) for the sensor
+ ctx.initializeAllNodes()
+ ctx.markDependencyNodesActive()
+
+ if err := ctx.createSensorResources(); err != nil {
+ ctx.logger.WithError(err).Errorln("failed to create resources for the sensor")
+ ctx.markSensorPhase(v1alpha1.NodePhaseError, false, err.Error())
+ return nil
}
+ ctx.markSensorPhase(v1alpha1.NodePhaseActive, false, "sensor is active")
+ ctx.logger.Infoln("successfully created resources for the sensor. sensor is in active state")
case v1alpha1.NodePhaseActive:
- soc.log.Info("sensor is running")
-
- err := soc.updateSensorResources()
- if err != nil {
+ ctx.logger.Infoln("checking for updates to the sensor object")
+ if err := ctx.updateSensorResources(); err != nil {
+ ctx.logger.WithError(err).Errorln("failed to update the sensor resources")
return err
}
+ ctx.updated = true
+ ctx.logger.Infoln("successfully processed sensor state update")
case v1alpha1.NodePhaseError:
- soc.log.Info("sensor is in error state. check sensor resource status information and corresponding escalated K8 event for the error")
-
- err := soc.updateSensorResources()
- if err != nil {
+ // If the sensor is in error state and if the sensor podTemplate spec has changed, then update the corresponding deployment
+ ctx.logger.Info("sensor is in error state, checking for updates to the sensor object")
+ if err := ctx.updateSensorResources(); err != nil {
+ ctx.logger.WithError(err).Errorln("failed to update the sensor resources")
return err
}
+ ctx.markSensorPhase(v1alpha1.NodePhaseActive, false, "sensor is active")
+ ctx.logger.Infoln("successfully processed the update")
}
+
return nil
}
-func (soc *sOperationCtx) createSensorResources() error {
- err := ValidateSensor(soc.s)
+// createSensorResources creates the K8s resources for a sensor object
+func (ctx *sensorContext) createSensorResources() error {
+ if ctx.sensor.Status.Resources == nil {
+ ctx.sensor.Status.Resources = &v1alpha1.SensorResources{}
+ }
+
+ ctx.logger.Infoln("generating deployment specification for the sensor")
+ deployment, err := ctx.deploymentBuilder()
if err != nil {
- soc.log.WithError(err).Error("failed to validate sensor")
- err = errors.Wrap(err, "failed to validate sensor")
- soc.markSensorPhase(v1alpha1.NodePhaseError, false, err.Error())
return err
}
-
- soc.initializeAllNodes()
- pod, err := soc.createSensorPod()
+ ctx.logger.WithField("name", deployment.Name).Infoln("creating the deployment resource for the sensor")
+ deployment, err = ctx.createDeployment(deployment)
if err != nil {
- err = errors.Wrap(err, "failed to create sensor pod")
- soc.markSensorPhase(v1alpha1.NodePhaseError, false, err.Error())
return err
}
- soc.markAllNodePhases()
- soc.log.WithField(common.LabelPodName, pod.Name).Info("sensor pod is created")
+ ctx.sensor.Status.Resources.Deployment = &deployment.ObjectMeta
- // expose sensor if service is configured
- if soc.srctx.getServiceTemplateSpec() != nil {
- svc, err := soc.createSensorService()
+ if ctx.sensor.Spec.EventProtocol.Type == apicommon.HTTP {
+ ctx.logger.Infoln("generating service specification for the sensor")
+ service, err := ctx.serviceBuilder()
if err != nil {
- err = errors.Wrap(err, "failed to create sensor service")
- soc.markSensorPhase(v1alpha1.NodePhaseError, false, err.Error())
return err
}
- soc.log.WithField(common.LabelServiceName, svc.Name).Info("sensor service is created")
- }
-
- // if we get here - we know the signals are running
- soc.log.Info("marking sensor as active")
- soc.markSensorPhase(v1alpha1.NodePhaseActive, false, "listening for events")
- return nil
-}
-func (soc *sOperationCtx) createSensorPod() (*corev1.Pod, error) {
- pod, err := soc.srctx.newSensorPod()
- if err != nil {
- soc.log.WithError(err).Error("failed to initialize pod for sensor")
- return nil, err
- }
- pod, err = soc.srctx.createSensorPod(pod)
- if err != nil {
- soc.log.WithError(err).Error("failed to create pod for sensor")
- return nil, err
- }
- return pod, nil
-}
-
-func (soc *sOperationCtx) createSensorService() (*corev1.Service, error) {
- svc, err := soc.srctx.newSensorService()
- if err != nil {
- soc.log.WithError(err).Error("failed to initialize service for sensor")
- return nil, err
- }
- svc, err = soc.srctx.createSensorService(svc)
- if err != nil {
- soc.log.WithError(err).Error("failed to create service for sensor")
- return nil, err
- }
- return svc, nil
-}
-
-func (soc *sOperationCtx) updateSensorResources() error {
- err := ValidateSensor(soc.s)
- if err != nil {
- soc.log.WithError(err).Error("failed to validate sensor")
- err = errors.Wrap(err, "failed to validate sensor")
- if soc.s.Status.Phase != v1alpha1.NodePhaseError {
- soc.markSensorPhase(v1alpha1.NodePhaseError, false, err.Error())
+ ctx.logger.WithField("name", service.Name).Infoln("generating deployment specification for the sensor")
+ service, err = ctx.createService(service)
+ if err != nil {
+ return err
}
+ ctx.sensor.Status.Resources.Service = &service.ObjectMeta
return err
}
+ return nil
+}
- _, podChanged, err := soc.updateSensorPod()
+// updateSensorResources updates the sensor resources
+func (ctx *sensorContext) updateSensorResources() error {
+ deployment, err := ctx.updateDeployment()
if err != nil {
- err = errors.Wrap(err, "failed to update sensor pod")
- soc.markSensorPhase(v1alpha1.NodePhaseError, false, err.Error())
return err
}
-
- _, svcChanged, err := soc.updateSensorService()
+ ctx.sensor.Status.Resources.Deployment = &deployment.ObjectMeta
+ service, err := ctx.updateService()
if err != nil {
- err = errors.Wrap(err, "failed to update sensor service")
- soc.markSensorPhase(v1alpha1.NodePhaseError, false, err.Error())
return err
}
-
- if soc.s.Status.Phase != v1alpha1.NodePhaseActive && (podChanged || svcChanged) {
- soc.markSensorPhase(v1alpha1.NodePhaseActive, false, "sensor is active")
+ if service == nil {
+ ctx.sensor.Status.Resources.Service = nil
+ return nil
}
-
+ ctx.sensor.Status.Resources.Service = &service.ObjectMeta
return nil
}
-func (soc *sOperationCtx) updateSensorPod() (*corev1.Pod, bool, error) {
- // Check if sensor spec has changed for pod.
- existingPod, err := soc.srctx.getSensorPod()
- if err != nil {
- soc.log.WithError(err).Error("failed to get pod for sensor")
- return nil, false, err
- }
-
- // create a new pod spec
- newPod, err := soc.srctx.newSensorPod()
- if err != nil {
- soc.log.WithError(err).Error("failed to initialize pod for sensor")
- return nil, false, err
- }
-
- // check if pod spec remained unchanged
- if existingPod != nil {
- if existingPod.Annotations != nil && existingPod.Annotations[common.AnnotationSensorResourceSpecHashName] == newPod.Annotations[common.AnnotationSensorResourceSpecHashName] {
- soc.log.WithField(common.LabelPodName, existingPod.Name).Debug("sensor pod spec unchanged")
- return nil, false, nil
+// updateSensorState updates the sensor resource state
+func (ctx *sensorContext) updateSensorState() {
+ if ctx.updated {
+ // persist updates to sensor resource
+ labels := map[string]string{
+ common.LabelSensorName: ctx.sensor.Name,
+ LabelPhase: string(ctx.sensor.Status.Phase),
+ LabelControllerInstanceID: ctx.controller.Config.InstanceID,
+ common.LabelOperation: "persist_state_update",
}
+ eventType := common.StateChangeEventType
- // By now we are sure that the spec changed, so lets go ahead and delete the exisitng sensor pod.
- soc.log.WithField(common.LabelPodName, existingPod.Name).Info("sensor pod spec changed")
-
- err := soc.srctx.deleteSensorPod(existingPod)
+ updatedSensor, err := PersistUpdates(ctx.controller.sensorClient, ctx.sensor, ctx.logger)
if err != nil {
- soc.log.WithError(err).Error("failed to delete pod for sensor")
- return nil, false, err
- }
-
- soc.log.WithField(common.LabelPodName, existingPod.Name).Info("sensor pod is deleted")
- }
-
- // Create new pod for updated sensor spec.
- createdPod, err := soc.srctx.createSensorPod(newPod)
- if err != nil {
- soc.log.WithError(err).Error("failed to create pod for sensor")
- return nil, false, err
- }
- soc.log.WithField(common.LabelPodName, newPod.Name).Info("sensor pod is created")
-
- return createdPod, true, nil
-}
-
-func (soc *sOperationCtx) updateSensorService() (*corev1.Service, bool, error) {
- // Check if sensor spec has changed for service.
- existingSvc, err := soc.srctx.getSensorService()
- if err != nil {
- soc.log.WithError(err).Error("failed to get service for sensor")
- return nil, false, err
- }
-
- // create a new service spec
- newSvc, err := soc.srctx.newSensorService()
- if err != nil {
- soc.log.WithError(err).Error("failed to initialize service for sensor")
- return nil, false, err
- }
-
- if existingSvc != nil {
- // updated spec doesn't have service defined, delete existing service.
- if newSvc == nil {
- if err := soc.srctx.deleteSensorService(existingSvc); err != nil {
- return nil, false, err
- }
- return nil, true, nil
- }
+ ctx.logger.WithError(err).Errorln("failed to persist sensor update")
- // check if service spec remained unchanged
- if existingSvc.Annotations[common.AnnotationSensorResourceSpecHashName] == newSvc.Annotations[common.AnnotationSensorResourceSpecHashName] {
- soc.log.WithField(common.LabelServiceName, existingSvc.Name).Debug("sensor service spec unchanged")
- return nil, false, nil
+ // escalate failure
+ eventType = common.EscalationEventType
}
- // service spec changed, delete existing service and create new one
- soc.log.WithField(common.LabelServiceName, existingSvc.Name).Info("sensor service spec changed")
-
- if err := soc.srctx.deleteSensorService(existingSvc); err != nil {
- return nil, false, err
+ // update sensor ref. in case of failure to persist updates, this is a deep copy of old sensor resource
+ ctx.sensor = updatedSensor
+
+ labels[common.LabelEventType] = string(eventType)
+ if err := common.GenerateK8sEvent(ctx.controller.k8sClient,
+ "persist update",
+ eventType,
+ "sensor state update",
+ ctx.sensor.Name,
+ ctx.sensor.Namespace,
+ ctx.controller.Config.InstanceID,
+ sensor.Kind,
+ labels); err != nil {
+ ctx.logger.WithError(err).Error("failed to create K8s event to logger sensor state persist operation")
+ return
}
- } else if newSvc == nil {
- // sensor service doesn't exist originally
- return nil, false, nil
- }
-
- // change createSensorService to take a service spec
- createdSvc, err := soc.srctx.createSensorService(newSvc)
- if err != nil {
- soc.log.WithField(common.LabelServiceName, newSvc.Name).WithError(err).Error("failed to create service for sensor")
- return nil, false, err
+ ctx.logger.Info("successfully persisted sensor resource update and created K8s event")
}
- soc.log.WithField(common.LabelServiceName, newSvc.Name).Info("sensor service is created")
-
- return createdSvc, true, nil
+ ctx.updated = false
}
// mark the overall sensor phase
-func (soc *sOperationCtx) markSensorPhase(phase v1alpha1.NodePhase, markComplete bool, message ...string) {
- justCompleted := soc.s.Status.Phase != phase
+func (ctx *sensorContext) markSensorPhase(phase v1alpha1.NodePhase, markComplete bool, message ...string) {
+ justCompleted := ctx.sensor.Status.Phase != phase
if justCompleted {
- soc.log.WithFields(
+ ctx.logger.WithFields(
map[string]interface{}{
- "old": string(soc.s.Status.Phase),
+ "old": string(ctx.sensor.Status.Phase),
"new": string(phase),
},
- ).Info("phase updated")
+ ).Infoln("phase updated")
- soc.s.Status.Phase = phase
- if soc.s.ObjectMeta.Labels == nil {
- soc.s.ObjectMeta.Labels = make(map[string]string)
+ ctx.sensor.Status.Phase = phase
+
+ if ctx.sensor.ObjectMeta.Labels == nil {
+ ctx.sensor.ObjectMeta.Labels = make(map[string]string)
}
- if soc.s.ObjectMeta.Annotations == nil {
- soc.s.ObjectMeta.Annotations = make(map[string]string)
+
+ if ctx.sensor.ObjectMeta.Annotations == nil {
+ ctx.sensor.ObjectMeta.Annotations = make(map[string]string)
}
- soc.s.ObjectMeta.Labels[common.LabelSensorKeyPhase] = string(phase)
- // add annotations so a resource sensor can watch this sensor.
- soc.s.ObjectMeta.Annotations[common.LabelSensorKeyPhase] = string(phase)
+
+ ctx.sensor.ObjectMeta.Labels[LabelPhase] = string(phase)
+ ctx.sensor.ObjectMeta.Annotations[LabelPhase] = string(phase)
}
- if soc.s.Status.StartedAt.IsZero() {
- soc.s.Status.StartedAt = metav1.Time{Time: time.Now().UTC()}
+
+ if ctx.sensor.Status.StartedAt.IsZero() {
+ ctx.sensor.Status.StartedAt = metav1.Time{Time: time.Now().UTC()}
}
- if len(message) > 0 && soc.s.Status.Message != message[0] {
- soc.log.WithFields(
+
+ if len(message) > 0 && ctx.sensor.Status.Message != message[0] {
+ ctx.logger.WithFields(
map[string]interface{}{
- "old": soc.s.Status.Message,
+ "old": ctx.sensor.Status.Message,
"new": message[0],
},
- ).Info("sensor message updated")
- soc.s.Status.Message = message[0]
+ ).Infoln("sensor message updated")
+
+ ctx.sensor.Status.Message = message[0]
}
switch phase {
- case v1alpha1.NodePhaseComplete, v1alpha1.NodePhaseError:
+ case v1alpha1.NodePhaseError:
if markComplete && justCompleted {
- soc.log.Info("marking sensor complete")
- soc.s.Status.CompletedAt = metav1.Time{Time: time.Now().UTC()}
- if soc.s.ObjectMeta.Labels == nil {
- soc.s.ObjectMeta.Labels = make(map[string]string)
+ ctx.logger.Infoln("marking sensor state as complete")
+ ctx.sensor.Status.CompletedAt = metav1.Time{Time: time.Now().UTC()}
+
+ if ctx.sensor.ObjectMeta.Labels == nil {
+ ctx.sensor.ObjectMeta.Labels = make(map[string]string)
+ }
+ if ctx.sensor.ObjectMeta.Annotations == nil {
+ ctx.sensor.ObjectMeta.Annotations = make(map[string]string)
}
- soc.s.ObjectMeta.Labels[common.LabelSensorKeyComplete] = "true"
- soc.s.ObjectMeta.Annotations[common.LabelSensorKeyComplete] = string(phase)
+
+ ctx.sensor.ObjectMeta.Labels[LabelComplete] = "true"
+ ctx.sensor.ObjectMeta.Annotations[LabelComplete] = string(phase)
}
}
- soc.updated = true
+ ctx.updated = true
}
-func (soc *sOperationCtx) initializeAllNodes() {
+// initializeAllNodes initializes nodes of all types within a sensor
+func (ctx *sensorContext) initializeAllNodes() {
// Initialize all event dependency nodes
- for _, dependency := range soc.s.Spec.Dependencies {
- InitializeNode(soc.s, dependency.Name, v1alpha1.NodeTypeEventDependency, soc.log)
+ for _, dependency := range ctx.sensor.Spec.Dependencies {
+ InitializeNode(ctx.sensor, dependency.Name, v1alpha1.NodeTypeEventDependency, ctx.logger)
}
// Initialize all dependency groups
- if soc.s.Spec.DependencyGroups != nil {
- for _, group := range soc.s.Spec.DependencyGroups {
- InitializeNode(soc.s, group.Name, v1alpha1.NodeTypeDependencyGroup, soc.log)
+ if ctx.sensor.Spec.DependencyGroups != nil {
+ for _, group := range ctx.sensor.Spec.DependencyGroups {
+ InitializeNode(ctx.sensor, group.Name, v1alpha1.NodeTypeDependencyGroup, ctx.logger)
}
}
// Initialize all trigger nodes
- for _, trigger := range soc.s.Spec.Triggers {
- InitializeNode(soc.s, trigger.Template.Name, v1alpha1.NodeTypeTrigger, soc.log)
+ for _, trigger := range ctx.sensor.Spec.Triggers {
+ InitializeNode(ctx.sensor, trigger.Template.Name, v1alpha1.NodeTypeTrigger, ctx.logger)
}
}
-func (soc *sOperationCtx) markAllNodePhases() {
+// markDependencyNodesActive marks phase of all dependencies and dependency groups as active
+func (ctx *sensorContext) markDependencyNodesActive() {
// Mark all event dependency nodes as active
- for _, dependency := range soc.s.Spec.Dependencies {
- MarkNodePhase(soc.s, dependency.Name, v1alpha1.NodeTypeEventDependency, v1alpha1.NodePhaseActive, nil, soc.log, "node is active")
+ for _, dependency := range ctx.sensor.Spec.Dependencies {
+ MarkNodePhase(ctx.sensor, dependency.Name, v1alpha1.NodeTypeEventDependency, v1alpha1.NodePhaseActive, nil, ctx.logger, "node is active")
}
// Mark all dependency groups as active
- if soc.s.Spec.DependencyGroups != nil {
- for _, group := range soc.s.Spec.DependencyGroups {
- MarkNodePhase(soc.s, group.Name, v1alpha1.NodeTypeDependencyGroup, v1alpha1.NodePhaseActive, nil, soc.log, "node is active")
+ if ctx.sensor.Spec.DependencyGroups != nil {
+ for _, group := range ctx.sensor.Spec.DependencyGroups {
+ MarkNodePhase(ctx.sensor, group.Name, v1alpha1.NodeTypeDependencyGroup, v1alpha1.NodePhaseActive, nil, ctx.logger, "node is active")
+ }
+ }
+}
+
+// PersistUpdates persists the updates to the Sensor resource
+func PersistUpdates(client sensorclientset.Interface, sensorObj *v1alpha1.Sensor, log *logrus.Logger) (*v1alpha1.Sensor, error) {
+ sensorClient := client.ArgoprojV1alpha1().Sensors(sensorObj.ObjectMeta.Namespace)
+ // in case persist update fails
+ oldsensor := sensorObj.DeepCopy()
+
+ sensorObj, err := sensorClient.Update(sensorObj)
+ if err != nil {
+ if errors.IsConflict(err) {
+ log.WithError(err).Error("error updating sensor")
+ return oldsensor, err
+ }
+
+ log.Infoln(err)
+ log.Infoln("re-applying updates on latest version and retrying update")
+ err = ReapplyUpdate(client, sensorObj)
+ if err != nil {
+ log.WithError(err).Error("failed to re-apply update")
+ return oldsensor, err
}
}
+ log.WithField(common.LabelPhase, string(sensorObj.Status.Phase)).Info("sensor state updated successfully")
+ return sensorObj, nil
+}
+
+// Reapply the update to sensor
+func ReapplyUpdate(sensorClient sensorclientset.Interface, sensor *v1alpha1.Sensor) error {
+ return wait.ExponentialBackoff(common.DefaultRetry, func() (bool, error) {
+ client := sensorClient.ArgoprojV1alpha1().Sensors(sensor.Namespace)
+ s, err := client.Update(sensor)
+ if err != nil {
+ if !common.IsRetryableKubeAPIError(err) {
+ return false, err
+ }
+ return false, nil
+ }
+ sensor = s
+ return true, nil
+ })
}
diff --git a/controllers/sensor/operator_test.go b/controllers/sensor/operator_test.go
index 7809e05c75..e81d804887 100644
--- a/controllers/sensor/operator_test.go
+++ b/controllers/sensor/operator_test.go
@@ -17,356 +17,146 @@ limitations under the License.
package sensor
import (
- "testing"
-
+ "github.com/argoproj/argo-events/common"
"github.com/argoproj/argo-events/pkg/apis/sensor/v1alpha1"
- "github.com/ghodss/yaml"
- "github.com/smartystreets/goconvey/convey"
- corev1 "k8s.io/api/core/v1"
+ "github.com/stretchr/testify/assert"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
- "k8s.io/client-go/tools/cache"
+ "testing"
)
-var sensorStr = `
-
-apiVersion: argoproj.io/v1alpha1
-kind: Sensor
-metadata:
- name: artifact-sensor
- namespace: argo-events
- labels:
- sensors.argoproj.io/sensor-controller-instanceid: argo-events
-spec:
- template:
- containers:
- - name: "sensor"
- image: "argoproj/sensor"
- imagePullPolicy: Always
- serviceAccountName: argo-events-sa
- dependencies:
- - name: artifact-gateway:input
- eventProtocol:
- type: "HTTP"
- http:
- port: "9300"
- triggers:
- - template:
- name: artifact-workflow-trigger
- group: argoproj.io
- version: v1alpha1
- kind: Workflow
- source:
- inline: |
- apiVersion: argoproj.io/v1alpha1
- kind: Workflow
- metadata:
- generateName: hello-world-
- spec:
- entrypoint: whalesay
- templates:
- -
- container:
- args:
- - "hello world"
- command:
- - cowsay
- image: "docker/whalesay:latest"
- name: whalesay
-`
+func TestOperate(t *testing.T) {
+ controller := getController()
+ ctx := newSensorContext(sensorObj.DeepCopy(), controller)
+ sensor, err := controller.sensorClient.ArgoprojV1alpha1().Sensors(sensorObj.Namespace).Create(sensorObj)
+ assert.Nil(t, err)
+ ctx.sensor = sensor.DeepCopy()
+
+ tests := []struct {
+ name string
+ updateFunc func()
+ testFunc func(oldMetadata *v1alpha1.SensorResources)
+ }{
+ {
+ name: "process a new sensor object",
+ updateFunc: func() {},
+ testFunc: func(oldMetadata *v1alpha1.SensorResources) {
+ assert.NotNil(t, ctx.sensor.Status.Resources)
+ metadata := ctx.sensor.Status.Resources
+ deployment, err := controller.k8sClient.AppsV1().Deployments(metadata.Deployment.Namespace).Get(metadata.Deployment.Name, metav1.GetOptions{})
+ assert.Nil(t, err)
+ assert.NotNil(t, deployment)
+ service, err := controller.k8sClient.CoreV1().Services(metadata.Service.Namespace).Get(metadata.Service.Name, metav1.GetOptions{})
+ assert.Nil(t, err)
+ assert.NotNil(t, service)
+ assert.Equal(t, v1alpha1.NodePhaseActive, ctx.sensor.Status.Phase)
+ assert.Equal(t, 2, len(ctx.sensor.Status.Nodes))
+ assert.Equal(t, "sensor is active", ctx.sensor.Status.Message)
+ },
+ },
+ {
+ name: "process a sensor object update",
+ updateFunc: func() {
+ ctx.sensor.Spec.Template.Spec.Containers[0].Name = "updated-name"
+ },
+ testFunc: func(oldMetadata *v1alpha1.SensorResources) {
+ assert.NotNil(t, ctx.sensor.Status.Resources)
+ metadata := ctx.sensor.Status.Resources
+ deployment, err := controller.k8sClient.AppsV1().Deployments(metadata.Deployment.Namespace).Get(metadata.Deployment.Name, metav1.GetOptions{})
+ assert.Nil(t, err)
+ assert.NotNil(t, deployment)
+ assert.NotEqual(t, oldMetadata.Deployment.Annotations[common.AnnotationResourceSpecHash], deployment.Annotations[common.AnnotationResourceSpecHash])
+ assert.Equal(t, deployment.Spec.Template.Spec.Containers[0].Name, "updated-name")
+ service, err := controller.k8sClient.CoreV1().Services(metadata.Service.Namespace).Get(metadata.Service.Name, metav1.GetOptions{})
+ assert.Nil(t, err)
+ assert.NotNil(t, service)
+ assert.Equal(t, oldMetadata.Service.Annotations[common.AnnotationResourceSpecHash], service.Annotations[common.AnnotationResourceSpecHash])
+ assert.Equal(t, v1alpha1.NodePhaseActive, ctx.sensor.Status.Phase)
+ assert.Equal(t, "sensor is active", ctx.sensor.Status.Message)
+ },
+ },
+ {
+ name: "process a sensor in error state",
+ updateFunc: func() {
+ ctx.sensor.Status.Phase = v1alpha1.NodePhaseError
+ ctx.sensor.Status.Message = "sensor is in error state"
+ ctx.sensor.Spec.Template.Spec.Containers[0].Name = "revert-name"
+ },
+ testFunc: func(oldMetadata *v1alpha1.SensorResources) {
+ assert.Equal(t, v1alpha1.NodePhaseActive, ctx.sensor.Status.Phase)
+ assert.Equal(t, "sensor is active", ctx.sensor.Status.Message)
+ },
+ },
+ }
-var (
- sensorPodName = "artifact-sensor"
- sensorSvcName = "artifact-sensor-svc"
-)
+ for _, test := range tests {
+ t.Run(test.name, func(t *testing.T) {
+ metadata := ctx.sensor.Status.Resources.DeepCopy()
+ test.updateFunc()
+ err := ctx.operate()
+ assert.Nil(t, err)
+ test.testFunc(metadata)
+ })
+ }
+}
-func getSensor() (*v1alpha1.Sensor, error) {
- var sensor *v1alpha1.Sensor
- err := yaml.Unmarshal([]byte(sensorStr), &sensor)
- return sensor, err
+func TestUpdateSensorState(t *testing.T) {
+ controller := getController()
+ ctx := newSensorContext(sensorObj.DeepCopy(), controller)
+ sensor, err := controller.sensorClient.ArgoprojV1alpha1().Sensors(sensorObj.Namespace).Create(sensorObj)
+ assert.Nil(t, err)
+ ctx.sensor = sensor.DeepCopy()
+ assert.Equal(t, v1alpha1.NodePhaseNew, ctx.sensor.Status.Phase)
+ ctx.sensor.Status.Phase = v1alpha1.NodePhaseActive
+ ctx.updated = true
+ ctx.updateSensorState()
+ assert.Equal(t, v1alpha1.NodePhaseActive, ctx.sensor.Status.Phase)
}
-func waitForAllInformers(done chan struct{}, controller *SensorController) {
- cache.WaitForCacheSync(done, controller.informer.HasSynced)
- cache.WaitForCacheSync(done, controller.podInformer.Informer().HasSynced)
- cache.WaitForCacheSync(done, controller.svcInformer.Informer().HasSynced)
+func TestMarkSensorPhase(t *testing.T) {
+ controller := getController()
+ ctx := newSensorContext(sensorObj.DeepCopy(), controller)
+ sensor, err := controller.sensorClient.ArgoprojV1alpha1().Sensors(sensorObj.Namespace).Create(sensorObj)
+ assert.Nil(t, err)
+ ctx.sensor = sensor.DeepCopy()
+ ctx.markSensorPhase(v1alpha1.NodePhaseActive, false, "sensor is active")
+ assert.Equal(t, v1alpha1.NodePhaseActive, ctx.sensor.Status.Phase)
+ assert.Equal(t, "sensor is active", ctx.sensor.Status.Message)
}
-func getPodAndService(controller *SensorController, namespace string) (*corev1.Pod, *corev1.Service, error) {
- pod, err := controller.kubeClientset.CoreV1().Pods(namespace).Get(sensorPodName, metav1.GetOptions{})
- if err != nil {
- return nil, nil, err
+func TestInitializeAllNodes(t *testing.T) {
+ controller := getController()
+ ctx := newSensorContext(sensorObj.DeepCopy(), controller)
+ ctx.initializeAllNodes()
+ for _, node := range ctx.sensor.Status.Nodes {
+ assert.Equal(t, v1alpha1.NodePhaseNew, node.Phase)
+ assert.NotEmpty(t, node.Name)
+ assert.NotEmpty(t, node.ID)
}
- svc, err := controller.kubeClientset.CoreV1().Services(namespace).Get(sensorSvcName, metav1.GetOptions{})
- if err != nil {
- return nil, nil, err
- }
- return pod, svc, err
}
-func deletePodAndService(controller *SensorController, namespace string) error {
- err := controller.kubeClientset.CoreV1().Pods(namespace).Delete(sensorPodName, &metav1.DeleteOptions{})
- if err != nil {
- return err
+func TestMarkDependencyNodesActive(t *testing.T) {
+ controller := getController()
+ ctx := newSensorContext(sensorObj.DeepCopy(), controller)
+ ctx.initializeAllNodes()
+ ctx.markDependencyNodesActive()
+ for _, node := range ctx.sensor.Status.Nodes {
+ if node.Type == v1alpha1.NodeTypeEventDependency {
+ assert.Equal(t, v1alpha1.NodePhaseActive, node.Phase)
+ } else {
+ assert.Equal(t, v1alpha1.NodePhaseNew, node.Phase)
+ }
}
- err = controller.kubeClientset.CoreV1().Services(namespace).Delete(sensorSvcName, &metav1.DeleteOptions{})
- return err
}
-func TestSensorOperations(t *testing.T) {
- done := make(chan struct{})
- convey.Convey("Given a sensor, parse it", t, func() {
- sensor, err := getSensor()
- convey.So(err, convey.ShouldBeNil)
- convey.So(sensor, convey.ShouldNotBeNil)
-
- controller := getSensorController()
- soc := newSensorOperationCtx(sensor, controller)
- convey.ShouldPanic(soc.log, nil)
- convey.So(soc, convey.ShouldNotBeNil)
-
- convey.Convey("Create the sensor", func() {
- sensor, err = controller.sensorClientset.ArgoprojV1alpha1().Sensors(sensor.Namespace).Create(sensor)
- convey.So(err, convey.ShouldBeNil)
- convey.So(sensor, convey.ShouldNotBeNil)
-
- convey.Convey("Operate on a new sensor", func() {
- soc.markSensorPhase(v1alpha1.NodePhaseNew, false, "test")
-
- waitForAllInformers(done, controller)
- err := soc.operate()
- convey.So(err, convey.ShouldBeNil)
- waitForAllInformers(done, controller)
-
- convey.Convey("Sensor should be marked as active with it's nodes initialized", func() {
- sensor, err = controller.sensorClientset.ArgoprojV1alpha1().Sensors(soc.s.Namespace).Get(soc.s.Name, metav1.GetOptions{})
- convey.So(err, convey.ShouldBeNil)
- convey.So(sensor, convey.ShouldNotBeNil)
- convey.So(sensor.Status.Phase, convey.ShouldEqual, v1alpha1.NodePhaseActive)
-
- for _, node := range soc.s.Status.Nodes {
- switch node.Type {
- case v1alpha1.NodeTypeEventDependency:
- convey.So(node.Phase, convey.ShouldEqual, v1alpha1.NodePhaseActive)
- case v1alpha1.NodeTypeDependencyGroup:
- convey.So(node.Phase, convey.ShouldEqual, v1alpha1.NodePhaseActive)
- case v1alpha1.NodeTypeTrigger:
- convey.So(node.Phase, convey.ShouldEqual, v1alpha1.NodePhaseNew)
- }
- }
- })
-
- convey.Convey("Sensor pod and service should be created", func() {
- sensorPod, sensorSvc, err := getPodAndService(controller, sensor.Namespace)
- convey.So(err, convey.ShouldBeNil)
- convey.So(sensorPod, convey.ShouldNotBeNil)
- convey.So(sensorSvc, convey.ShouldNotBeNil)
-
- convey.Convey("Go to active state", func() {
- sensor, err := controller.sensorClientset.ArgoprojV1alpha1().Sensors(sensor.Namespace).Get(sensor.Name, metav1.GetOptions{})
- convey.So(err, convey.ShouldBeNil)
- convey.So(sensor.Status.Phase, convey.ShouldEqual, v1alpha1.NodePhaseActive)
- })
- })
- })
-
- convey.Convey("Operate on sensor in active state", func() {
- err := controller.sensorClientset.ArgoprojV1alpha1().Sensors(sensor.Namespace).Delete(sensor.Name, &metav1.DeleteOptions{})
- convey.So(err, convey.ShouldBeNil)
- sensor, err = controller.sensorClientset.ArgoprojV1alpha1().Sensors(sensor.Namespace).Create(sensor)
- convey.So(err, convey.ShouldBeNil)
- convey.So(sensor, convey.ShouldNotBeNil)
-
- soc.markSensorPhase(v1alpha1.NodePhaseNew, false, "test")
-
- // Operate it once to create pod and service
- waitForAllInformers(done, controller)
- err = soc.operate()
- convey.So(err, convey.ShouldBeNil)
- waitForAllInformers(done, controller)
-
- sensorPod, sensorSvc, err := getPodAndService(controller, sensor.Namespace)
- convey.So(err, convey.ShouldBeNil)
- convey.So(sensorPod, convey.ShouldNotBeNil)
- convey.So(sensorSvc, convey.ShouldNotBeNil)
-
- convey.Convey("Operation must succeed", func() {
- soc.markSensorPhase(v1alpha1.NodePhaseActive, false, "test")
-
- waitForAllInformers(done, controller)
- err := soc.operate()
- convey.So(err, convey.ShouldBeNil)
- waitForAllInformers(done, controller)
-
- convey.Convey("Untouch pod and service", func() {
- sensorPod, sensorSvc, err := getPodAndService(controller, sensor.Namespace)
- convey.So(err, convey.ShouldBeNil)
- convey.So(sensorPod, convey.ShouldNotBeNil)
- convey.So(sensorSvc, convey.ShouldNotBeNil)
-
- convey.Convey("Stay in active state", func() {
- sensor, err := controller.sensorClientset.ArgoprojV1alpha1().Sensors(sensor.Namespace).Get(sensor.Name, metav1.GetOptions{})
- convey.So(err, convey.ShouldBeNil)
- convey.So(sensor.Status.Phase, convey.ShouldEqual, v1alpha1.NodePhaseActive)
- })
- })
- })
-
- convey.Convey("With deleted pod and service", func() {
- err := deletePodAndService(controller, sensor.Namespace)
- convey.So(err, convey.ShouldBeNil)
-
- convey.Convey("Operation must succeed", func() {
- soc.markSensorPhase(v1alpha1.NodePhaseActive, false, "test")
-
- waitForAllInformers(done, controller)
- err := soc.operate()
- convey.So(err, convey.ShouldBeNil)
- waitForAllInformers(done, controller)
-
- convey.Convey("Create pod and service", func() {
- sensorPod, sensorSvc, err := getPodAndService(controller, sensor.Namespace)
- convey.So(err, convey.ShouldBeNil)
- convey.So(sensorPod, convey.ShouldNotBeNil)
- convey.So(sensorSvc, convey.ShouldNotBeNil)
-
- convey.Convey("Stay in active state", func() {
- sensor, err := controller.sensorClientset.ArgoprojV1alpha1().Sensors(sensor.Namespace).Get(sensor.Name, metav1.GetOptions{})
- convey.So(err, convey.ShouldBeNil)
- convey.So(sensor.Status.Phase, convey.ShouldEqual, v1alpha1.NodePhaseActive)
- })
- })
- })
- })
-
- convey.Convey("Change pod and service spec", func() {
- soc.srctx.s.Spec.Template.Spec.RestartPolicy = "Never"
- soc.srctx.s.Spec.EventProtocol.Http.Port = "1234"
-
- sensorPod, sensorSvc, err := getPodAndService(controller, sensor.Namespace)
- convey.So(err, convey.ShouldBeNil)
- convey.So(sensorPod, convey.ShouldNotBeNil)
- convey.So(sensorSvc, convey.ShouldNotBeNil)
-
- convey.Convey("Operation must succeed", func() {
- soc.markSensorPhase(v1alpha1.NodePhaseActive, false, "test")
-
- waitForAllInformers(done, controller)
- err := soc.operate()
- convey.So(err, convey.ShouldBeNil)
- waitForAllInformers(done, controller)
-
- convey.Convey("Recreate pod and service", func() {
- sensorPod, sensorSvc, err := getPodAndService(controller, sensor.Namespace)
- convey.So(err, convey.ShouldBeNil)
- convey.So(sensorPod.Spec.RestartPolicy, convey.ShouldEqual, "Never")
- convey.So(sensorSvc.Spec.Ports[0].TargetPort.IntVal, convey.ShouldEqual, 1234)
-
- convey.Convey("Stay in active state", func() {
- sensor, err := controller.sensorClientset.ArgoprojV1alpha1().Sensors(sensor.Namespace).Get(sensor.Name, metav1.GetOptions{})
- convey.So(err, convey.ShouldBeNil)
- convey.So(sensor.Status.Phase, convey.ShouldEqual, v1alpha1.NodePhaseActive)
- })
- })
- })
- })
- })
-
- convey.Convey("Operate on sensor in error state", func() {
- err := controller.sensorClientset.ArgoprojV1alpha1().Sensors(sensor.Namespace).Delete(sensor.Name, &metav1.DeleteOptions{})
- convey.So(err, convey.ShouldBeNil)
- sensor, err = controller.sensorClientset.ArgoprojV1alpha1().Sensors(sensor.Namespace).Create(sensor)
- convey.So(err, convey.ShouldBeNil)
- convey.So(sensor, convey.ShouldNotBeNil)
-
- soc.markSensorPhase(v1alpha1.NodePhaseNew, false, "test")
-
- // Operate it once to create pod and service
- waitForAllInformers(done, controller)
- err = soc.operate()
- convey.So(err, convey.ShouldBeNil)
- waitForAllInformers(done, controller)
-
- convey.Convey("Operation must succeed", func() {
- soc.markSensorPhase(v1alpha1.NodePhaseError, false, "test")
-
- waitForAllInformers(done, controller)
- err := soc.operate()
- convey.So(err, convey.ShouldBeNil)
- waitForAllInformers(done, controller)
-
- convey.Convey("Untouch pod and service", func() {
- sensorPod, sensorSvc, err := getPodAndService(controller, sensor.Namespace)
- convey.So(err, convey.ShouldBeNil)
- convey.So(sensorPod, convey.ShouldNotBeNil)
- convey.So(sensorSvc, convey.ShouldNotBeNil)
-
- convey.Convey("Stay in error state", func() {
- sensor, err := controller.sensorClientset.ArgoprojV1alpha1().Sensors(sensor.Namespace).Get(sensor.Name, metav1.GetOptions{})
- convey.So(err, convey.ShouldBeNil)
- convey.So(sensor.Status.Phase, convey.ShouldEqual, v1alpha1.NodePhaseError)
- })
- })
- })
-
- convey.Convey("With deleted pod and service", func() {
- err := deletePodAndService(controller, sensor.Namespace)
- convey.So(err, convey.ShouldBeNil)
-
- convey.Convey("Operation must succeed", func() {
- soc.markSensorPhase(v1alpha1.NodePhaseError, false, "test")
-
- waitForAllInformers(done, controller)
- err := soc.operate()
- convey.So(err, convey.ShouldBeNil)
- waitForAllInformers(done, controller)
-
- convey.Convey("Create pod and service", func() {
- sensorPod, sensorSvc, err := getPodAndService(controller, sensor.Namespace)
- convey.So(err, convey.ShouldBeNil)
- convey.So(sensorPod, convey.ShouldNotBeNil)
- convey.So(sensorSvc, convey.ShouldNotBeNil)
-
- convey.Convey("Go to active state", func() {
- sensor, err := controller.sensorClientset.ArgoprojV1alpha1().Sensors(sensor.Namespace).Get(sensor.Name, metav1.GetOptions{})
- convey.So(err, convey.ShouldBeNil)
- convey.So(sensor.Status.Phase, convey.ShouldEqual, v1alpha1.NodePhaseActive)
- })
- })
- })
- })
-
- convey.Convey("Change pod and service spec", func() {
- soc.srctx.s.Spec.Template.Spec.RestartPolicy = "Never"
- soc.srctx.s.Spec.EventProtocol.Http.Port = "1234"
-
- sensorPod, sensorSvc, err := getPodAndService(controller, sensor.Namespace)
- convey.So(err, convey.ShouldBeNil)
- convey.So(sensorPod, convey.ShouldNotBeNil)
- convey.So(sensorSvc, convey.ShouldNotBeNil)
-
- convey.Convey("Operation must succeed", func() {
- soc.markSensorPhase(v1alpha1.NodePhaseError, false, "test")
-
- waitForAllInformers(done, controller)
- err := soc.operate()
- convey.So(err, convey.ShouldBeNil)
- waitForAllInformers(done, controller)
-
- convey.Convey("Recreate pod and service", func() {
- sensorPod, sensorSvc, err := getPodAndService(controller, sensor.Namespace)
- convey.So(err, convey.ShouldBeNil)
- convey.So(sensorPod.Spec.RestartPolicy, convey.ShouldEqual, "Never")
- convey.So(sensorSvc.Spec.Ports[0].TargetPort.IntVal, convey.ShouldEqual, 1234)
-
- convey.Convey("Go to active state", func() {
- sensor, err := controller.sensorClientset.ArgoprojV1alpha1().Sensors(sensor.Namespace).Get(sensor.Name, metav1.GetOptions{})
- convey.So(err, convey.ShouldBeNil)
- convey.So(sensor.Status.Phase, convey.ShouldEqual, v1alpha1.NodePhaseActive)
- })
- })
- })
- })
- })
- })
- })
+func TestPersistUpdates(t *testing.T) {
+ controller := getController()
+ ctx := newSensorContext(sensorObj.DeepCopy(), controller)
+ sensor, err := controller.sensorClient.ArgoprojV1alpha1().Sensors(sensorObj.Namespace).Create(sensorObj)
+ assert.Nil(t, err)
+ ctx.sensor = sensor.DeepCopy()
+ ctx.sensor.Spec.Circuit = "fake-group"
+ sensor, err = PersistUpdates(controller.sensorClient, ctx.sensor.DeepCopy(), ctx.logger)
+ assert.Nil(t, err)
+ assert.Equal(t, "fake-group", sensor.Spec.Circuit)
+ assert.Equal(t, "fake-group", ctx.sensor.Spec.Circuit)
}
diff --git a/controllers/sensor/resource.go b/controllers/sensor/resource.go
index 83545344d0..d9d12d1e0c 100644
--- a/controllers/sensor/resource.go
+++ b/controllers/sensor/resource.go
@@ -1,182 +1,178 @@
+/*
+Copyright 2018 BlackRock, Inc.
+
+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.
+*/
+
package sensor
import (
"github.com/argoproj/argo-events/common"
controllerscommon "github.com/argoproj/argo-events/controllers/common"
- pc "github.com/argoproj/argo-events/pkg/apis/common"
+ apicommon "github.com/argoproj/argo-events/pkg/apis/common"
"github.com/argoproj/argo-events/pkg/apis/sensor/v1alpha1"
+ "github.com/pkg/errors"
+ appv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
+ apierror "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
- "k8s.io/apimachinery/pkg/labels"
- "k8s.io/apimachinery/pkg/selection"
"k8s.io/apimachinery/pkg/util/intstr"
)
-type sResourceCtx struct {
- // s is the gateway-controller object
- s *v1alpha1.Sensor
- // reference to the gateway-controller-controller
- controller *SensorController
-
- controllerscommon.ChildResourceContext
+// generateServiceSpec returns a K8s service spec for the sensor
+func (ctx *sensorContext) generateServiceSpec() *corev1.Service {
+ return &corev1.Service{
+ ObjectMeta: metav1.ObjectMeta{
+ Labels: map[string]string{
+ common.LabelSensorName: ctx.sensor.Name,
+ LabelControllerInstanceID: ctx.controller.Config.InstanceID,
+ },
+ },
+ Spec: corev1.ServiceSpec{
+ Ports: []corev1.ServicePort{
+ {
+ Port: intstr.Parse(ctx.sensor.Spec.EventProtocol.Http.Port).IntVal,
+ TargetPort: intstr.FromInt(int(intstr.Parse(ctx.sensor.Spec.EventProtocol.Http.Port).IntVal)),
+ },
+ },
+ Type: corev1.ServiceTypeClusterIP,
+ Selector: map[string]string{
+ common.LabelOwnerName: ctx.sensor.Name,
+ },
+ },
+ }
}
-// NewSensorResourceContext returns new sResourceCtx
-func NewSensorResourceContext(s *v1alpha1.Sensor, controller *SensorController) sResourceCtx {
- return sResourceCtx{
- s: s,
- controller: controller,
- ChildResourceContext: controllerscommon.ChildResourceContext{
- SchemaGroupVersionKind: v1alpha1.SchemaGroupVersionKind,
- LabelOwnerName: common.LabelSensorName,
- LabelKeyOwnerControllerInstanceID: common.LabelKeySensorControllerInstanceID,
- AnnotationOwnerResourceHashName: common.AnnotationSensorResourceSpecHashName,
- InstanceID: controller.Config.InstanceID,
- },
+// serviceBuilder builds a new service that exposes sensor.
+func (ctx *sensorContext) serviceBuilder() (*corev1.Service, error) {
+ service := ctx.generateServiceSpec()
+ if err := controllerscommon.SetObjectMeta(ctx.sensor, service, v1alpha1.SchemaGroupVersionKind); err != nil {
+ return nil, err
}
+ return service, nil
}
-// sensorResourceLabelSelector returns label selector of the sensor of the context
-func (src *sResourceCtx) sensorResourceLabelSelector() (labels.Selector, error) {
- req, err := labels.NewRequirement(common.LabelSensorName, selection.Equals, []string{src.s.Name})
- if err != nil {
+// deploymentBuilder builds the deployment specification for the sensor
+func (ctx *sensorContext) deploymentBuilder() (*appv1.Deployment, error) {
+ replicas := int32(1)
+ podTemplateSpec := ctx.sensor.Spec.Template.DeepCopy()
+ if podTemplateSpec.Labels == nil {
+ podTemplateSpec.Labels = map[string]string{}
+ }
+ podTemplateSpec.Labels[common.LabelOwnerName] = ctx.sensor.Name
+ deployment := &appv1.Deployment{
+ ObjectMeta: podTemplateSpec.ObjectMeta,
+ Spec: appv1.DeploymentSpec{
+ Template: *podTemplateSpec,
+ Replicas: &replicas,
+ Selector: &metav1.LabelSelector{
+ MatchLabels: podTemplateSpec.Labels,
+ },
+ },
+ }
+ envVars := []corev1.EnvVar{
+ {
+ Name: common.SensorName,
+ Value: ctx.sensor.Name,
+ },
+ {
+ Name: common.SensorNamespace,
+ Value: ctx.sensor.Namespace,
+ },
+ {
+ Name: common.EnvVarControllerInstanceID,
+ Value: ctx.controller.Config.InstanceID,
+ },
+ }
+ for i, container := range deployment.Spec.Template.Spec.Containers {
+ container.Env = append(container.Env, envVars...)
+ deployment.Spec.Template.Spec.Containers[i] = container
+ }
+ if err := controllerscommon.SetObjectMeta(ctx.sensor, deployment, v1alpha1.SchemaGroupVersionKind); err != nil {
return nil, err
}
- return labels.NewSelector().Add(*req), nil
+ return deployment, nil
}
-// createSensorService creates a service
-func (src *sResourceCtx) createSensorService(svc *corev1.Service) (*corev1.Service, error) {
- return src.controller.kubeClientset.CoreV1().Services(src.s.Namespace).Create(svc)
+// createDeployment creates a deployment for the sensor
+func (ctx *sensorContext) createDeployment(deployment *appv1.Deployment) (*appv1.Deployment, error) {
+ return ctx.controller.k8sClient.AppsV1().Deployments(deployment.Namespace).Create(deployment)
}
-// deleteSensorService deletes a given service
-func (src *sResourceCtx) deleteSensorService(svc *corev1.Service) error {
- return src.controller.kubeClientset.CoreV1().Services(src.s.Namespace).Delete(svc.Name, &metav1.DeleteOptions{})
+// createService creates a service for the sensor
+func (ctx *sensorContext) createService(service *corev1.Service) (*corev1.Service, error) {
+ return ctx.controller.k8sClient.CoreV1().Services(service.Namespace).Create(service)
}
-// getSensorService returns the service of sensor
-func (src *sResourceCtx) getSensorService() (*corev1.Service, error) {
- selector, err := src.sensorResourceLabelSelector()
- if err != nil {
- return nil, err
- }
- svcs, err := src.controller.svcInformer.Lister().Services(src.s.Namespace).List(selector)
+// updateDeployment updates the deployment for the sensor
+func (ctx *sensorContext) updateDeployment() (*appv1.Deployment, error) {
+ newDeployment, err := ctx.deploymentBuilder()
if err != nil {
return nil, err
}
- if len(svcs) == 0 {
- return nil, nil
- }
- return svcs[0], nil
-}
-// newSensorService returns a new service that exposes sensor.
-func (src *sResourceCtx) newSensorService() (*corev1.Service, error) {
- serviceTemplateSpec := src.getServiceTemplateSpec()
- if serviceTemplateSpec == nil {
- return nil, nil
+ currentMetadata := ctx.sensor.Status.Resources.Deployment
+ if currentMetadata == nil {
+ return nil, errors.New("deployment metadata is expected to be set in gateway object")
}
- service := &corev1.Service{
- ObjectMeta: serviceTemplateSpec.ObjectMeta,
- Spec: serviceTemplateSpec.Spec,
- }
- if service.Namespace == "" {
- service.Namespace = src.s.Namespace
- }
- if service.Name == "" {
- service.Name = common.DefaultServiceName(src.s.Name)
- }
- err := src.SetObjectMeta(src.s, service)
- return service, err
-}
-// getSensorPod returns the pod of sensor
-func (src *sResourceCtx) getSensorPod() (*corev1.Pod, error) {
- selector, err := src.sensorResourceLabelSelector()
- if err != nil {
- return nil, err
- }
- pods, err := src.controller.podInformer.Lister().Pods(src.s.Namespace).List(selector)
+ currentDeployment, err := ctx.controller.k8sClient.AppsV1().Deployments(currentMetadata.Namespace).Get(currentMetadata.Name, metav1.GetOptions{})
if err != nil {
+ if apierror.IsNotFound(err) {
+ return ctx.controller.k8sClient.AppsV1().Deployments(newDeployment.Namespace).Create(newDeployment)
+ }
return nil, err
}
- if len(pods) == 0 {
- return nil, nil
- }
- return pods[0], nil
-}
-// createSensorPod creates a pod of sensor
-func (src *sResourceCtx) createSensorPod(pod *corev1.Pod) (*corev1.Pod, error) {
- return src.controller.kubeClientset.CoreV1().Pods(src.s.Namespace).Create(pod)
+ if currentDeployment.Annotations != nil && currentDeployment.Annotations[common.AnnotationResourceSpecHash] != newDeployment.Annotations[common.AnnotationResourceSpecHash] {
+ if err := ctx.controller.k8sClient.AppsV1().Deployments(currentDeployment.Namespace).Delete(currentDeployment.Name, &metav1.DeleteOptions{}); err != nil {
+ return nil, err
+ }
+ return ctx.controller.k8sClient.AppsV1().Deployments(newDeployment.Namespace).Create(newDeployment)
+ }
+ return currentDeployment, nil
}
-// deleteSensorPod deletes a given pod
-func (src *sResourceCtx) deleteSensorPod(pod *corev1.Pod) error {
- return src.controller.kubeClientset.CoreV1().Pods(src.s.Namespace).Delete(pod.Name, &metav1.DeleteOptions{})
-}
+// updateService updates the service for the sensor
+func (ctx *sensorContext) updateService() (*corev1.Service, error) {
+ isHttpTransport := ctx.sensor.Spec.EventProtocol.Type == apicommon.HTTP
+ currentMetadata := ctx.sensor.Status.Resources.Service
-// newSensorPod returns a new pod of sensor
-func (src *sResourceCtx) newSensorPod() (*corev1.Pod, error) {
- podTemplateSpec := src.s.Spec.Template.DeepCopy()
- pod := &corev1.Pod{
- ObjectMeta: podTemplateSpec.ObjectMeta,
- Spec: podTemplateSpec.Spec,
+ if currentMetadata == nil && !isHttpTransport {
+ return nil, nil
}
- if pod.Namespace == "" {
- pod.Namespace = src.s.Namespace
+ if currentMetadata != nil && !isHttpTransport {
+ if err := ctx.controller.k8sClient.CoreV1().Services(currentMetadata.Namespace).Delete(currentMetadata.Name, &metav1.DeleteOptions{}); err != nil {
+ // warning is sufficient instead of halting the entire sensor operation by marking it as failed.
+ ctx.logger.WithField("service-name", currentMetadata.Name).WithError(err).Warnln("failed to delete the current service")
+ }
+ return nil, nil
}
- if pod.Name == "" {
- pod.Name = src.s.Name
+ newService, err := ctx.serviceBuilder()
+ if err != nil {
+ return nil, err
}
- src.setupContainersForSensorPod(pod)
- err := src.SetObjectMeta(src.s, pod)
- return pod, err
-}
-
-// containers required for sensor deployment
-func (src *sResourceCtx) setupContainersForSensorPod(pod *corev1.Pod) {
- // env variables
- envVars := []corev1.EnvVar{
- {
- Name: common.SensorName,
- Value: src.s.Name,
- },
- {
- Name: common.SensorNamespace,
- Value: src.s.Namespace,
- },
- {
- Name: common.EnvVarSensorControllerInstanceID,
- Value: src.controller.Config.InstanceID,
- },
+ if currentMetadata == nil && isHttpTransport {
+ return ctx.controller.k8sClient.CoreV1().Services(newService.Namespace).Create(newService)
}
- for i, container := range pod.Spec.Containers {
- container.Env = append(container.Env, envVars...)
- pod.Spec.Containers[i] = container
+ if currentMetadata == nil {
+ return nil, nil
}
-}
-
-func (src *sResourceCtx) getServiceTemplateSpec() *pc.ServiceTemplateSpec {
- var serviceSpec *pc.ServiceTemplateSpec
- // Create a ClusterIP service to expose sensor in cluster if the event protocol type is HTTP
- if src.s.Spec.EventProtocol.Type == pc.HTTP {
- serviceSpec = &pc.ServiceTemplateSpec{
- Spec: corev1.ServiceSpec{
- Ports: []corev1.ServicePort{
- {
- Port: intstr.Parse(src.s.Spec.EventProtocol.Http.Port).IntVal,
- TargetPort: intstr.FromInt(int(intstr.Parse(src.s.Spec.EventProtocol.Http.Port).IntVal)),
- },
- },
- Type: corev1.ServiceTypeClusterIP,
- Selector: map[string]string{
- common.LabelSensorName: src.s.Name,
- common.LabelKeySensorControllerInstanceID: src.controller.Config.InstanceID,
- },
- },
+ if currentMetadata.Annotations != nil && currentMetadata.Annotations[common.AnnotationResourceSpecHash] != newService.Annotations[common.AnnotationResourceSpecHash] {
+ if err := ctx.controller.k8sClient.CoreV1().Services(currentMetadata.Namespace).Delete(currentMetadata.Name, &metav1.DeleteOptions{}); err != nil {
+ return nil, err
}
+ return ctx.controller.k8sClient.CoreV1().Services(newService.Namespace).Create(newService)
}
- return serviceSpec
+ return ctx.controller.k8sClient.CoreV1().Services(currentMetadata.Namespace).Get(currentMetadata.Name, metav1.GetOptions{})
}
diff --git a/controllers/sensor/resource_test.go b/controllers/sensor/resource_test.go
new file mode 100644
index 0000000000..3cc3b1b569
--- /dev/null
+++ b/controllers/sensor/resource_test.go
@@ -0,0 +1,170 @@
+/*
+Copyright 2018 BlackRock, Inc.
+
+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.
+*/
+
+package sensor
+
+import (
+ "testing"
+
+ "github.com/argoproj/argo-events/common"
+ apicommon "github.com/argoproj/argo-events/pkg/apis/common"
+ "github.com/argoproj/argo-events/pkg/apis/sensor/v1alpha1"
+ "github.com/stretchr/testify/assert"
+ corev1 "k8s.io/api/core/v1"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+)
+
+var sensorObj = &v1alpha1.Sensor{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "fake-sensor",
+ Namespace: "faker",
+ },
+ Spec: v1alpha1.SensorSpec{
+ Template: &corev1.PodTemplateSpec{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "fake-sensor",
+ Namespace: "faker",
+ },
+ Spec: corev1.PodSpec{
+ Containers: []corev1.Container{
+ {
+ Name: "fake-sensor",
+ ImagePullPolicy: corev1.PullAlways,
+ Image: "argoproj/sensor",
+ },
+ },
+ },
+ },
+ EventProtocol: &apicommon.EventProtocol{
+ Http: apicommon.Http{
+ Port: "12000",
+ },
+ Type: apicommon.HTTP,
+ },
+ Triggers: []v1alpha1.Trigger{
+ {
+ Template: &v1alpha1.TriggerTemplate{
+ Name: "fake-trigger",
+ GroupVersionResource: &metav1.GroupVersionResource{
+ Group: "k8s.io",
+ Version: "",
+ Resource: "pods",
+ },
+ Source: &v1alpha1.ArtifactLocation{},
+ },
+ },
+ },
+ Dependencies: []v1alpha1.EventDependency{
+ {
+ Name: "fake-gateway:fake-one",
+ },
+ },
+ },
+}
+
+func TestResource_BuildService(t *testing.T) {
+ controller := getController()
+ opctx := newSensorContext(sensorObj.DeepCopy(), controller)
+ service, err := opctx.serviceBuilder()
+ assert.Nil(t, err)
+ assert.NotNil(t, service)
+ assert.NotEmpty(t, service.Annotations[common.AnnotationResourceSpecHash])
+}
+
+func TestResource_BuildDeployment(t *testing.T) {
+ controller := getController()
+ opctx := newSensorContext(sensorObj.DeepCopy(), controller)
+ deployment, err := opctx.deploymentBuilder()
+ assert.Nil(t, err)
+ assert.NotNil(t, deployment)
+ assert.NotEmpty(t, deployment.Annotations[common.AnnotationResourceSpecHash])
+ assert.Equal(t, int(*deployment.Spec.Replicas), 1)
+}
+
+func TestResource_SetupContainers(t *testing.T) {
+ controller := getController()
+ opctx := newSensorContext(sensorObj.DeepCopy(), controller)
+ deployment, err := opctx.deploymentBuilder()
+ assert.Nil(t, err)
+ assert.NotNil(t, deployment)
+ assert.Equal(t, deployment.Spec.Template.Spec.Containers[0].Env[0].Name, common.SensorName)
+ assert.Equal(t, deployment.Spec.Template.Spec.Containers[0].Env[0].Value, opctx.sensor.Name)
+ assert.Equal(t, deployment.Spec.Template.Spec.Containers[0].Env[1].Name, common.SensorNamespace)
+ assert.Equal(t, deployment.Spec.Template.Spec.Containers[0].Env[1].Value, opctx.sensor.Namespace)
+ assert.Equal(t, deployment.Spec.Template.Spec.Containers[0].Env[2].Name, common.EnvVarControllerInstanceID)
+ assert.Equal(t, deployment.Spec.Template.Spec.Containers[0].Env[2].Value, controller.Config.InstanceID)
+}
+
+func TestResource_UpdateResources(t *testing.T) {
+ controller := getController()
+ ctx := newSensorContext(sensorObj.DeepCopy(), controller)
+ err := ctx.createSensorResources()
+ assert.Nil(t, err)
+
+ tests := []struct {
+ name string
+ updateFunc func()
+ testFunc func(t *testing.T, oldResources *v1alpha1.SensorResources)
+ }{
+ {
+ name: "update deployment when sensor template is updated",
+ updateFunc: func() {
+ ctx.sensor.Spec.Template.Spec.Containers[0].ImagePullPolicy = corev1.PullIfNotPresent
+ },
+ testFunc: func(t *testing.T, oldResources *v1alpha1.SensorResources) {
+ oldDeployment := oldResources.Deployment
+ deployment, err := ctx.controller.k8sClient.AppsV1().Deployments(ctx.sensor.Status.Resources.Deployment.Namespace).Get(ctx.sensor.Status.Resources.Deployment.Name, metav1.GetOptions{})
+ assert.Nil(t, err)
+ assert.NotNil(t, deployment)
+ assert.NotEqual(t, oldDeployment.Annotations[common.AnnotationResourceSpecHash], deployment.Annotations[common.AnnotationResourceSpecHash])
+
+ oldService := oldResources.Service
+ service, err := ctx.controller.k8sClient.CoreV1().Services(ctx.sensor.Status.Resources.Service.Namespace).Get(ctx.sensor.Status.Resources.Service.Name, metav1.GetOptions{})
+ assert.Nil(t, err)
+ assert.NotNil(t, service)
+ assert.Equal(t, oldService.Annotations[common.AnnotationResourceSpecHash], service.Annotations[common.AnnotationResourceSpecHash])
+ },
+ },
+ {
+ name: "update event protocol to NATS and check the service deletion",
+ updateFunc: func() {
+ ctx.sensor.Spec.EventProtocol.Type = apicommon.NATS
+ },
+ testFunc: func(t *testing.T, oldResources *v1alpha1.SensorResources) {
+ oldDeployment := oldResources.Deployment
+ deployment, err := ctx.controller.k8sClient.AppsV1().Deployments(ctx.sensor.Status.Resources.Deployment.Namespace).Get(ctx.sensor.Status.Resources.Deployment.Name, metav1.GetOptions{})
+ assert.Nil(t, err)
+ assert.NotNil(t, deployment)
+ assert.Equal(t, oldDeployment.Annotations[common.AnnotationResourceSpecHash], deployment.Annotations[common.AnnotationResourceSpecHash])
+
+ oldService := oldResources.Service
+ service, err := ctx.controller.k8sClient.CoreV1().Services(oldService.Namespace).Get(oldService.Name, metav1.GetOptions{})
+ assert.NotNil(t, err)
+ assert.Nil(t, service)
+ },
+ },
+ }
+
+ for _, test := range tests {
+ oldResources := ctx.sensor.Status.Resources.DeepCopy()
+ t.Run(test.name, func(t *testing.T) {
+ test.updateFunc()
+ err := ctx.updateSensorResources()
+ assert.Nil(t, err)
+ test.testFunc(t, oldResources)
+ })
+ }
+}
diff --git a/controllers/sensor/state_test.go b/controllers/sensor/state_test.go
deleted file mode 100644
index 3142d1ff3b..0000000000
--- a/controllers/sensor/state_test.go
+++ /dev/null
@@ -1,65 +0,0 @@
-package sensor
-
-import (
- "testing"
-
- "github.com/argoproj/argo-events/common"
- apicommon "github.com/argoproj/argo-events/pkg/apis/common"
- "github.com/argoproj/argo-events/pkg/apis/sensor/v1alpha1"
- fakesensor "github.com/argoproj/argo-events/pkg/client/sensor/clientset/versioned/fake"
- "github.com/smartystreets/goconvey/convey"
- metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-)
-
-func TestSensorState(t *testing.T) {
- fakeSensorClient := fakesensor.NewSimpleClientset()
- logger := common.NewArgoEventsLogger()
- sn := &v1alpha1.Sensor{
- ObjectMeta: metav1.ObjectMeta{
- Name: "test-sensor",
- Namespace: "test",
- },
- }
-
- convey.Convey("Given a sensor", t, func() {
- convey.Convey("Create the sensor", func() {
- sn, err := fakeSensorClient.ArgoprojV1alpha1().Sensors(sn.Namespace).Create(sn)
- convey.So(err, convey.ShouldBeNil)
- convey.So(sn, convey.ShouldNotBeNil)
- })
-
- convey.Convey("Initialize a new node", func() {
- status := InitializeNode(sn, "first_node", v1alpha1.NodeTypeEventDependency, logger)
- convey.So(status.Phase, convey.ShouldEqual, v1alpha1.NodePhaseNew)
- })
-
- convey.Convey("Persist updates to sn", func() {
- sensor, err := PersistUpdates(fakeSensorClient, sn, "1", logger)
- convey.So(err, convey.ShouldBeNil)
- convey.So(len(sensor.Status.Nodes), convey.ShouldEqual, 1)
- })
-
- convey.Convey("Mark sn node state to active", func() {
- status := MarkNodePhase(sn, "first_node", v1alpha1.NodeTypeEventDependency, v1alpha1.NodePhaseActive, &apicommon.Event{
- Payload: []byte("test payload"),
- }, logger)
- convey.So(status.Phase, convey.ShouldEqual, v1alpha1.NodePhaseActive)
- })
-
- convey.Convey("Reapply sn update", func() {
- err := ReapplyUpdate(fakeSensorClient, sn)
- convey.So(err, convey.ShouldBeNil)
- })
-
- convey.Convey("Fetch sn and check updates are applied", func() {
- sensor, err := fakeSensorClient.ArgoprojV1alpha1().Sensors(sn.Namespace).Get(sn.Name, metav1.GetOptions{})
- convey.So(err, convey.ShouldBeNil)
- convey.So(len(sensor.Status.Nodes), convey.ShouldEqual, 1)
- convey.Convey("Get the first_node node", func() {
- node := GetNodeByName(sensor, "first_node")
- convey.So(node, convey.ShouldNotBeNil)
- convey.So(node.Phase, convey.ShouldEqual, v1alpha1.NodePhaseActive)
- })
- })
- })
-}
diff --git a/controllers/sensor/validate.go b/controllers/sensor/validate.go
index 5c1045e27a..abce9a72cb 100644
--- a/controllers/sensor/validate.go
+++ b/controllers/sensor/validate.go
@@ -22,7 +22,6 @@ import (
"time"
"github.com/Knetic/govaluate"
-
"github.com/argoproj/argo-events/common"
pc "github.com/argoproj/argo-events/pkg/apis/common"
"github.com/argoproj/argo-events/pkg/apis/sensor/v1alpha1"
diff --git a/controllers/sensor/validate_test.go b/controllers/sensor/validate_test.go
index 55ae563275..590b50eb88 100644
--- a/controllers/sensor/validate_test.go
+++ b/controllers/sensor/validate_test.go
@@ -20,26 +20,22 @@ import (
"fmt"
"github.com/argoproj/argo-events/pkg/apis/sensor/v1alpha1"
"github.com/ghodss/yaml"
+ "github.com/stretchr/testify/assert"
"io/ioutil"
"testing"
-
- "github.com/smartystreets/goconvey/convey"
)
func TestValidateSensor(t *testing.T) {
dir := "../../examples/sensors"
- convey.Convey("Validate list of sensor", t, func() {
- files, err := ioutil.ReadDir(dir)
- convey.So(err, convey.ShouldBeNil)
- for _, file := range files {
- fmt.Println("filename: ", file.Name())
- content, err := ioutil.ReadFile(fmt.Sprintf("%s/%s", dir, file.Name()))
- convey.So(err, convey.ShouldBeNil)
- var sensor *v1alpha1.Sensor
- err = yaml.Unmarshal([]byte(content), &sensor)
- convey.So(err, convey.ShouldBeNil)
- err = ValidateSensor(sensor)
- convey.So(err, convey.ShouldBeNil)
- }
- })
+ files, err := ioutil.ReadDir(dir)
+ assert.Nil(t, err)
+ for _, file := range files {
+ content, err := ioutil.ReadFile(fmt.Sprintf("%s/%s", dir, file.Name()))
+ assert.Nil(t, err)
+ var sensor *v1alpha1.Sensor
+ err = yaml.Unmarshal([]byte(content), &sensor)
+ assert.Nil(t, err)
+ err = ValidateSensor(sensor)
+ assert.Nil(t, err)
+ }
}
diff --git a/docs/assets/argo.png b/docs/assets/argo.png
new file mode 100644
index 0000000000000000000000000000000000000000..1560ce2490938837d3144d6e8f8c40999dbb73a4
GIT binary patch
literal 27999
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
W*E`f2G#(tGLGT4up4<;}7
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