diff --git a/docs/overview/elafros-api-objects.md b/docs/overview/elafros-api-objects.md index 498b40fc4add..71cd71430266 100644 --- a/docs/overview/elafros-api-objects.md +++ b/docs/overview/elafros-api-objects.md @@ -10,6 +10,9 @@ * Route * Assigns traffic to Revisions (fractional scaling or by name). * Built using [Istio](https://istio.io/docs/). +* Service + * Acts as a top-level controller to orchestrate Route and Configuration. + * Provides a simple entry point for UI and CLI tooling to achieve common behavior. * Build * Executes builds. diff --git a/docs/overview/elafros-api-service.md b/docs/overview/elafros-api-service.md new file mode 100644 index 000000000000..87d2519e0ad5 --- /dev/null +++ b/docs/overview/elafros-api-service.md @@ -0,0 +1,12 @@ +# API Objects - Service + +* Orchestrates Routes and Configurations to support higher-level tooling. + * Configuration of code + inbound routing in a single API call. + * Definition of specific common rollout policies + * Automatic (latest) rollout + * Manual rollout + * Extensible for other rollout strategies which might be requested + * Provides a top-level resource for graphical UI to key off of. +* Recommended starting point for Elafros use, as it provides "guide rails" to avoid unusual and non-idiomatic usage. + * Mirrors labels and annotations to the created objects. + * Advanced scenarios may require disconnecting the Service ownership of the created Route and Configuration. diff --git a/docs/overview/elafros-code-layout-controller.md b/docs/overview/elafros-code-layout-controller.md index 937991efc51b..7e5c0ebca4f7 100644 --- a/docs/overview/elafros-code-layout-controller.md +++ b/docs/overview/elafros-code-layout-controller.md @@ -1,6 +1,6 @@ # Code layout - Controller -* ./pkg/controller/{route,configuration,revision}/controller.go -* sync_handler the main method for reconcile +* `./pkg/controller/{configuration,revision,route,service}/controller.go` +* `sync_handler` is the main method for reconcile * Other resource creation broken into their own files in those directories -* Communicates information via [resource].status field +* Communicates information via `[resource].status` field diff --git a/docs/overview/elafros-code-layout-webhook.md b/docs/overview/elafros-code-layout-webhook.md index 7913c1242916..5183a4c8a2a6 100644 --- a/docs/overview/elafros-code-layout-webhook.md +++ b/docs/overview/elafros-code-layout-webhook.md @@ -1,11 +1,11 @@ # Code layout - Webhook -* ./pkg/webhook/webhook.go +* `./pkg/webhook/webhook.go` * All the generic mutations / validations * Serialization / deserialization * Calls into resource specific validation / mutation - * ./pkg/webhook/{route,revision,configuration} + * `./pkg/webhook/{configuration,revision,route,service}` * Mutations specified with an array of jsonpatch objects -* ./pkg/webhook/certs.go +* `./pkg/webhook/certs.go` * CA / Server cert diff --git a/docs/overview/images/api-objects.png b/docs/overview/images/api-objects.png index 205ce8a56aac..3cef80846cfa 100644 Binary files a/docs/overview/images/api-objects.png and b/docs/overview/images/api-objects.png differ diff --git a/docs/overview/index.md b/docs/overview/index.md index 362d6e53ba50..47cbfba85cbb 100644 --- a/docs/overview/index.md +++ b/docs/overview/index.md @@ -7,6 +7,7 @@ * [Configuration](./elafros-api-configuration.md) * [Revision](./elafros-api-revision.md) * [Route](./elafros-api-route.md) + * [Service](./elafros-api-service.md) * [Components](./elafros-components.md) * Code layout * [Resources](./elafros-code-layout-resources.md) diff --git a/docs/spec/errors.md b/docs/spec/errors.md index 6e657575d90b..89e074388ad0 100644 --- a/docs/spec/errors.md +++ b/docs/spec/errors.md @@ -60,25 +60,38 @@ should follow these patterns: Example user and system error scenarios are included below along with how the status is presented to CLI and UI tools via the API. -* [Revision failed to become Ready](#revision-failed-to-become-ready) -* [Build failed](#build-failed) -* [Revision not found by Route](#revision-not-found-by-route) -* [Configuration not found by Route](#configuration-not-found-by-route) -* [Latest Revision of a Configuration deleted](#latest-revision-of-a-configuration-deleted) -* [Resource exhausted while creating a revision](#resource-exhausted-while-creating-a-revision) -* [Deployment progressing slowly/stuck](#deployment-progressing-slowly-stuck) -* [Traffic shift progressing slowly/stuck](#traffic-shift-progressing-slowly-stuck) -* [Container image not present in repository](#container-image-not-present-in-repository) -* [Container image fails at startup on Revision](#container-image-fails-at-startup-on-revision) +* [Deployment-Related Failures](#deployment-related-failures) + * [Revision failed to become Ready](#revision-failed-to-become-ready) + * [Build failed](#build-failed) + * [Resource exhausted while creating a revision](#resource-exhausted-while-creating-a-revision) + * [Container image not present in repository](#container-image-not-present-in-repository) + * [Container image fails at startup on Revision](#container-image-fails-at-startup-on-revision) + * [Deployment progressing slowly/stuck](#deployment-progressing-slowly-stuck) +* [Routing-Related Failures](#routing-related-failures) + * [Traffic not assigned](#traffic-not-assigned) + * [Revision not found by Route](#revision-not-found-by-route) + * [Configuration not found by Route](#configuration-not-found-by-route) + * [Latest Revision of a Configuration deleted](#latest-revision-of-a-configuration-deleted) + * [Traffic shift progressing slowly/stuck](#traffic-shift-progressing-slowly-stuck) + + +# Deployment-Related Failures + +The following scenarios will generally occur when attempting to deploy +changes to the software stack by updating the Service or Configuration +resources to cause a new Revision to be created. ## Revision failed to become Ready -If the latest Revision fails to become `Ready` for any reason within some reasonable -timeframe, the Configuration should signal this -with the `LatestRevisionReady` status, copying the reason and the message -from the `Ready` condition on the Revision. +If the latest Revision fails to become `Ready` for any reason within +some reasonable timeframe, the Configuration and Service should signal +this with the `LatestRevisionReady` status, copying the reason and the +message from the `Ready` condition on the Revision. +```http +GET /api/elafros.dev/v1alpha1/namespaces/default/configurations/my-service +``` ```yaml ... status: @@ -87,8 +100,25 @@ status: conditions: - type: LatestRevisionReady status: False - reason: ContainerHealthy - message: "Unable to start because container is missing and build failed." + reason: BuildFailed + meassage: "Build Step XYZ failed with error message: $LASTLOGLINE" +``` + +```http +GET /api/elafros.dev/v1alpha1/namespaces/default/services/my-service +``` +```yaml +... +status: + latestReadyRevisionName: abc + latestCreatedRevisionName: bcd # Hasn't become "Ready" + conditions: + - type: Ready + status: True # If an earlier version is serving + - type: LatestRevisionReady + status: False + reason: BuildFailed + meassage: "Build Step XYZ failed with error message: $LASTLOGLINE" ``` @@ -116,7 +146,6 @@ status: message: "Step XYZ failed with error message: $LASTLOGLINE" ``` - ```http GET /apis/elafros.dev/v1alpha1/namespaces/default/revisions/abc ``` @@ -135,6 +164,182 @@ status: ``` +## Resource exhausted while creating a revision + +Since a Revision is only metadata, the Revision will be created, but +will have a condition indicating the underlying failure, possibly +indicating the failed underlying resource. In a multitenant +environment, the customer might not have have access or visibility +into the underlying resources in the hosting environment. + +```http +GET /apis/elafros.dev/v1alpha1/namespaces/default/revisions/abc +``` +```yaml +... +status: + conditions: + - type: Ready + status: False + reason: NoDeployment + message: "The controller could not create a deployment named ela-abc-e13ac." + - type: ResourcesProvisioned + status: False + reason: NoDeployment + message: "The controller could not create a deployment named ela-abc-e13ac." +``` + + +## Container image not present in repository + +Revisions might be created while a Build is still creating the +container image or uploading it to the repository. If the build is +being performed by a CRD in the cluster, the `spec.buildName` +attribute will be set (and see the [Build failed](#build-failed) +example). In other cases when the build is not supplied, the container +image referenced might not be present in the registry (either because +of a typo or because it was deleted). In this case, the `Ready` +condition will be set to `False` with a reason of +`ContainerMissing`. This condition could be corrected if the image +becomes available at a later time. Elafros could also make a defensive +copy of the container image to avoid having to surface this error if +the original docker image is deleted. + +```http +GET /apis/elafros.dev/v1alpha1/namespaces/default/revisions/abc +``` +```yaml +... +status: + conditions: + - type: Ready + status: False + reason: ContainerMissing + message: "Unable to fetch image 'gcr.io/...': " + - type: ContainerHealthy + status: False + reason: ContainerMissing + message: "Unable to fetch image 'gcr.io/...': " +``` + + +## Container image fails at startup on Revision + +Particularly for development cases with interpreted languages like +Node or Python, syntax errors might only be caught at container +startup time. For this reason, implementations should start a copy of +the container on deployment, before marking the container `Ready`. If +this container fails to start, the `Ready` condition will be set to +`False`, the reason will be set to `ExitCode%d` with the exit code of +the application, and the termination message from the container will +be provided. (Containers will be run with the default +`terminationMessagePath` and a `terminationMessagePolicy` of +`FallbackToLogsOnError`.) Additionally, the Revision `status.logsUrl` +should be present, which provides the address of an endpoint which can +be used to fetch the logs for the failed process. + +```http +GET /apis/elafros.dev/v1alpha1/namespaces/default/revisions/abc +``` +```yaml +... +status: + logUrl: "http://logging.infra.mycompany.com/...?filter=revision=abc&..." + conditions: + - type: Ready + status: False + reason: ExitCode127 + message: "Container failed with: SyntaxError: Unexpected identifier" + - type: ContainerHealthy + status: False + reason: ExitCode127 + message: "Container failed with: SyntaxError: Unexpected identifier" +``` + + +## Deployment progressing slowly/stuck + +See [the kubernetes documentation for how this is handled for +Deployments](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#failed-deployment). For +Revisions, we will start by assuming a single timeout for deployment +(rather than configurable), and report that the Revision was not Ready, +with a reason `ProgressDeadlineExceeded`. Note that we will only report +`ProgressDeadlineExceeded` if we could not determine another reason (such +as quota failures, missing build, or container execution failures). + +Since container setup time also affects the ability of 0 to 1 +autoscaling, the `Ready` failure with `ProgressDeadlineExceeded` +reason should be considered a terminal condition, even if Kubernetes +might attempt to make progress even after the deadline. + +```http +GET /apis/elafros.dev/v1alpha1/namespaces/default/revisions/abc +``` +```yaml +... +status: + conditions: + - type: Ready + status: False + reason: ProgressDeadlineExceeded + message: "Did not pass readiness checks in 120 seconds." +``` + + +# Routing-Related Failures + +The following scenarios are most likely to occur when attempting to +roll out a change by shifting traffic to a new Revision. Some of these +conditions can also occur under normal operations due to (for example) +operator error causing live resources to be deleted. + + +## Traffic not assigned + +If some percentage of traffic cannot be assigned to a live +(materialized or scaled-to-zero) Revision, the Route will report the +`Ready` condition as `False`. The Service will mirror this status in +its' `Ready` condition. For example, for a newly-created Service where +the first Revision is unable to serve: + +```http +GET /apis/elafros.dev/v1alpha1/namespaces/default/routes/my-service +``` +```yaml +... +status: + domain: my-service.default.mydomain.com + traffic: + - revisionName: "Not found" + percent: 100 + conditions: + - type: Ready + status: False + reason: RevisionMissing + message: "The configuration 'abc' does not have a LatestReadyRevision." +``` + +```http +GET /apis/elafros.dev/v1alpha1/namespaces/default/services/my-service +``` +```yaml +... +status: + latestCreatedRevisionname: abc + # no latestReadyRevisionName, because abc failed + domain: my-service.default.mydomain.com + conditions: + - type: Ready + status: False + reason: RevisionMissing + message: "The configuration 'abc' does not have a LatestReadyRevision." + - type: LatestRevisionReady + status: False + reason: ExitCode127 + message: "Container failed with: SyntaxError: Unexpected identifier" +``` + + ## Revision not found by Route If a Revision is referenced in the Route's `spec.traffic`, the @@ -143,7 +348,7 @@ found", and the `AllTrafficAssigned` condition will be marked as False with a reason of `RevisionMissing`. ```http -GET /apis/elafros.dev/v1alpha1/namespaces/default/routes/abc +GET /apis/elafros.dev/v1alpha1/namespaces/default/routes/my-service ``` ```yaml ... @@ -176,7 +381,7 @@ and the Configuration cannot be found, the corresponding entry in of `ConfigurationMissing`. ```http -GET /apis/elafros.dev/v1alpha1/namespaces/default/routes/abc +GET /apis/elafros.dev/v1alpha1/namespaces/default/routes/my-service ``` ```yaml ... @@ -228,61 +433,6 @@ status: ``` -## Resource exhausted while creating a revision - -Since a Revision is only metadata, the Revision will be created, but -will have a condition indicating the underlying failure, possibly -indicating the failed underlying resource. In a multitenant -environment, the customer might not have have access or visibility -into the underlying resources in the hosting environment. - -```http -GET /apis/elafros.dev/v1alpha1/namespaces/default/revisions/abc -``` -```yaml -... -status: - conditions: - - type: Ready - status: False - reason: NoDeployment - message: "The controller could not create a deployment named ela-abc-e13ac." - - type: ResourcesProvisioned - status: False - reason: NoDeployment - message: "The controller could not create a deployment named ela-abc-e13ac." -``` - - -## Deployment progressing slowly/stuck - -See -[the kubernetes documentation for how this is handled for Deployments](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#failed-deployment). For -Revisions, we will start by assuming a single timeout for deployment -(rather than configurable), and report that the Revision was not Ready, -with a reason `ProgressDeadlineExceeded`. Note that we will only report -`ProgressDeadlineExceeded` if we could not determine another reason (such -as quota failures, missing build, or container execution failures). - -Since container setup time also affects the ability of 0 to 1 -autoscaling, the `Ready` failure with `ProgressDeadlineExceeded` -reason should be considered a terminal condition, even if Kubernetes -might attempt to make progress even after the deadline. - -```http -GET /apis/elafros.dev/v1alpha1/namespaces/default/revisions/abc -``` -```yaml -... -status: - conditions: - - type: Ready - status: False - reason: ProgressDeadlineExceeded - message: "Did not pass readiness checks in 120 seconds." -``` - - ## Traffic shift progressing slowly/stuck Similar to deployment slowness, if the transfer of traffic (either via @@ -291,7 +441,7 @@ complete/update, the `RolloutInProgress` condition will remain at True, but the reason will be set to `ProgressDeadlineExceeded`. ```http -GET /apis/elafros.dev/v1alpha1/namespaces/default/routes/abc +GET /apis/elafros.dev/v1alpha1/namespaces/default/routes/my-service ``` ```yaml ... @@ -308,70 +458,3 @@ status: # reason is a short status, message provides error details message: "Unable to update traffic split for more than 120 seconds." ``` - - -## Container image not present in repository - -Revisions might be created while a Build is still creating the -container image or uploading it to the repository. If the build is -being performed by a CRD in the cluster, the `spec.buildName` -attribute will be set (and see the [Build failed](#build-failed) -example). In other cases when the build is not supplied, the container -image referenced might not be present in the registry (either because -of a typo or because it was deleted). In this case, the `Ready` -condition will be set to `False` with a reason of -`ContainerMissing`. This condition could be corrected if the image -becomes available at a later time. Elafros could also make a defensive -copy of the container image to avoid having to surface this error if -the original docker image is deleted. - -```http -GET /apis/elafros.dev/v1alpha1/namespaces/default/revisions/abc -``` -```yaml -... -status: - conditions: - - type: Ready - status: False - reason: ContainerMissing - message: "Unable to fetch image 'gcr.io/...': " - - type: ContainerHealthy - status: False - reason: ContainerMissing - message: "Unable to fetch image 'gcr.io/...': " -``` - - -## Container image fails at startup on Revision - -Particularly for development cases with interpreted languages like -Node or Python, syntax errors might only be caught at container -startup time. For this reason, implementations should start a copy of -the container on deployment, before marking the container `Ready`. If -this container fails to start, the `Ready` condition will be set to -`False`, the reason will be set to `ExitCode%d` with the exit code of -the application, and the termination message from the container will -be provided. (Containers will be run with the default -`terminationMessagePath` and a `terminationMessagePolicy` of -`FallbackToLogsOnError`.) Additionally, the Revision `status.logsUrl` -should be present, which provides the address of an endpoint which can -be used to fetch the logs for the failed process. - -```http -GET /apis/elafros.dev/v1alpha1/namespaces/default/revisions/abc -``` -```yaml -... -status: - logUrl: "http://logging.infra.mycompany.com/...?filter=revision=abc&..." - conditions: - - type: Ready - status: False - reason: ExitCode127 - message: "Container failed with: SyntaxError: Unexpected identifier" - - type: ContainerHealthy - status: False - reason: ExitCode127 - message: "Container failed with: SyntaxError: Unexpected identifier" -``` diff --git a/docs/spec/images/auto_rollout.png b/docs/spec/images/auto_rollout.png index 04dfb62c4fd8..9bf5b63bb143 100644 Binary files a/docs/spec/images/auto_rollout.png and b/docs/spec/images/auto_rollout.png differ diff --git a/docs/spec/images/build_example.png b/docs/spec/images/build_example.png index 28fb83e2e3bb..e3c7ff332a50 100644 Binary files a/docs/spec/images/build_example.png and b/docs/spec/images/build_example.png differ diff --git a/docs/spec/images/build_function.png b/docs/spec/images/build_function.png index d37bd8023861..e66ce4ec81c3 100644 Binary files a/docs/spec/images/build_function.png and b/docs/spec/images/build_function.png differ diff --git a/docs/spec/images/initial_creation.png b/docs/spec/images/initial_creation.png index 61e160c3d73b..1bf4a4a99e40 100644 Binary files a/docs/spec/images/initial_creation.png and b/docs/spec/images/initial_creation.png differ diff --git a/docs/spec/images/manual_rollout.png b/docs/spec/images/manual_rollout.png index 1e54b0a276d0..660f3695f5f7 100644 Binary files a/docs/spec/images/manual_rollout.png and b/docs/spec/images/manual_rollout.png differ diff --git a/docs/spec/images/object_model.png b/docs/spec/images/object_model.png index c48a4df5329c..0c58f1504f18 100644 Binary files a/docs/spec/images/object_model.png and b/docs/spec/images/object_model.png differ diff --git a/docs/spec/normative_examples.md b/docs/spec/normative_examples.md index a74681f2310d..7f57e7e931f6 100644 --- a/docs/spec/normative_examples.md +++ b/docs/spec/normative_examples.md @@ -8,8 +8,7 @@ Examples in this section illustrate: * [Automatic rollout of a new Revision to an existing Service with a pre-built container](#1-automatic-rollout-of-a-new-revision-to-existing-service---pre-built-container) -* [Creating a first route to deploy a first revision from a pre-built - container](#2-creating-route-and-deploying-first-revision---pre-built-container) +* [Creating a new Service with a pre-built container](#2-creating-a-new-service-with-a-pre-built-container) * [Configuration changes and manual rollout options](#3-manual-rollout-of-a-new-revision---config-change-only) * [Creating a revision from source](#4-deploy-a-revision-from-source) @@ -38,22 +37,23 @@ $ elafros deploy --service my-service **Steps**: -* Update the Configuration with the config change +* Update the Service with the config change **Results:** -* A new Revision is created, and automatically rolled out to 100% once - ready +* The Configuration associated with the Service is updated, and a new + Revision is created, and automatically rolled out to 100% once ready. ![Automatic Rollout](images/auto_rollout.png) -After the initial Route and Configuration have been created (which is -shown in the [second example](TODO)), the typical -interaction is to update the revision configuration, resulting in the -creation of a new revision, which will be automatically rolled out by -the route. Revision configuration updates can be handled as either a -PUT or PATCH operation: +After the initial Route and Configuration have been created (which is shown +in the +[second example](#2-creating-a-new-service-with-a-pre-built-container)), +the typical interaction is to update the revision configuration, resulting +in the creation of a new revision, which will be automatically rolled out by +the route. Revision configuration updates can be handled as either a PUT or +PATCH operation: * Optimistic concurrency controls for PUT operations in a read/modify/write routine work as expected in kubernetes. @@ -69,9 +69,28 @@ followed by several GET calls to illustrate each step in the reconciliation process as the system materializes the new revision, and begins shifting traffic from the old revision to the new revision. -The client PATCHes the configuration's template revision with just the -new container image, inheriting previous configuration from the -configuration: +The client PATCHes the service's configuration with new container image, +inheriting previous environment values from the configuration spec: + +```http +PATCH /apis/elafros.dev/v1alpha1/namespaces/default/services/my-service +``` +```yaml +apiVersion: elafros.dev/v1alpha1 +kind: Service +metadata: + name: my-service +spec: + runLatest: + configuration: + revisionTemplate: # template for building Revision + spec: + container: + image: gcr.io/... # new image +``` + +This causes the controller to PATCH the configuration's template revision +with the new container image: ```http PATCH /apis/elafros.dev/v1alpha1/namespaces/default/configurations/my-service @@ -80,7 +99,7 @@ PATCH /apis/elafros.dev/v1alpha1/namespaces/default/configurations/my-service apiVersion: elafros.dev/v1alpha1 kind: Configuration metadata: - name: my-service # by convention, same name as the service + name: my-service # Named the same as the Service spec: revisionTemplate: # template for building Revision spec: @@ -88,8 +107,8 @@ spec: image: gcr.io/... # new image ``` -The update to the Configuration triggers a new revision being created, -and the Configuration is updated to reflect the new Revision: +The update to the Configuration triggers a new Revision being created, and +the Configuration and Service are updated to reflect the new Revision: ```http GET /apis/elafros.dev/v1alpha1/namespaces/default/configurations/my-service @@ -110,6 +129,25 @@ status: observedGeneration: 1235 ``` +```http +GET /apis/elafros.dev/v1alpha1/namespaces/default/service/my-service +``` +```yaml +apiVersion: elafros.dev/v1alpha1 +kind: Service +metadata: + name: my-service + generation: 1452 + ... + +spec: + ... # same as before, except new container.image +status: + latestReadyRevisionName: abc + latestCreatedRevisionName: def # new revision created, but not ready yet + observedGeneration: 1452 +``` + The newly created revision has the same config as the previous revision, but different code. Note the generation label reflects the new generation of the configuration (1235), indicating the provenance @@ -145,13 +183,13 @@ status: ``` When the new revision is Ready, i.e. underlying resources are -materialized and ready to serve, the configuration updates its -`status.latestReadyRevisionName` status to reflect the new +materialized and ready to serve, the configuration (and service) +updates their `status.latestReadyRevisionName` to reflect the new revision. The route, which is configured to automatically rollout new revisions from the configuration, watches the configuration and is -notified of the `latestReadyRevisionName`, and begins migrating traffic -to it. During reconciliation, traffic may be routed to both existing -revision `abc` and new revision `def`: +notified of the `latestReadyRevisionName`, and begins migrating +traffic to it. During reconciliation, traffic may be routed to both +existing revision `abc` and new revision `def`: ```http GET /apis/elafros.dev/v1alpha1/namespaces/default/routes/my-service @@ -211,9 +249,9 @@ status: ``` -## 2) Creating Route and deploying first Revision - pre-built container +## 2) Creating a new Service with a pre-built container -**Scenario**: User creates a new Route and deploys their first +**Scenario**: User creates a new Service and deploys their first Revision based on a pre-built container ``` @@ -229,8 +267,8 @@ $ elafros deploy --service my-service --region us-central1 **Steps**: -* Create a new Configuration and a Route that references a that - configuration. +* Create a new Service. That Service will trigger creation of a new + Configuration and a Route that references that configuration. **Results**: @@ -239,38 +277,66 @@ $ elafros deploy --service my-service --region us-central1 * A new Route is created, referencing the configuration -* The route begins serving traffic to the revision that was created by +* The route begins serving traffic to the Revision that was created by the configuration ![Initial Creation](images/initial_creation.png) -The previous example assumed an existing Route and Configuration to -illustrate the common scenario of updating the configuration to deploy -a new revision to the service. +The previous example assumed an existing Service with a Route and +Configuration to illustrate the common scenario of updating the +configuration to deploy a new revision to the service. In this getting started example, deploying a first Revision is -accomplished by creating a new Configuration (which will generate a -new Revision) and creating a new Route referring to that -configuration. Note that these two steps can occur in either order, or -in parallel. - -A Route can either refer directly to a Revision, or to the latest -ready revision of a Configuration, as this example illustrates. This -is the most straightforward scenario that many Elafros customers are -expected to use, and is consistent with the experience of deploying -code that is rolled out immediately. +accomplished by creating a new Service, which will create both a +Configuration and a new Route referring to that configuration. In +turn, the Configuration will generate a new Revision. Note that these +steps may occur in in parallel. + +In the `runLatest` style of Service, the Route always references the +latest ready revision of a Configuration, as this example +illustrates. This is the most straightforward scenario that many +Elafros customers are expected to use, and is consistent with the +experience of deploying code that is rolled out immediately. A Route +may also directly reference a Revision, which is shown in +[example 3](#3-manual-rollout-of-a-new-revision---config-change-only). The example shows the POST calls issued by the client, followed by several GET calls to illustrate each step in the reconciliation process as the system materializes and begins routing traffic to the revision. -The client creates the route and configuration, which by convention -share the same name: +The client creates the service in `runLatest` mode: + +```http +POST /apis/elafros.dev/v1alpha1/namespaces/default/services +``` +```yaml +apiVersion: elafros.dev/v1alpha1 +kind: Service +metadata: + name: my-service +spec: + runLatest: + configuration: + revisionTemplate: # template for building Revision + metadata: ... + spec: + container: # k8s core.v1.Container + image: gcr.io/... + env: + - name: FOO + value: bar + - name: HELLO + value: world + ... +``` + +This causes the service controller to create route and configuration +objects with the same name as the Service: ```http -POST /apis/elafros.dev/v1alpha1/namespaces/default/routes +GET /apis/elafros.dev/v1alpha1/namespaces/default/routes ``` ```yaml apiVersion: elafros.dev/v1alpha1 @@ -285,7 +351,7 @@ spec: ``` ```http -POST /apis/elafros.dev/v1alpha1/namespaces/default/configurations +GET /apis/elafros.dev/v1alpha1/namespaces/default/configurations ``` ```yaml apiVersion: elafros.dev/v1alpha1 @@ -294,8 +360,8 @@ metadata: name: my-service # By convention (not req'd), same name as the service. # This will also be set as the "elafros.dev/configuration" # label on the created Revision. -spec: - revisionTemplate: # template for building Revision +spec: # Contents from service's spec.runLatest.configuration + revisionTemplate: metadata: ... spec: container: # k8s core.v1.Container @@ -308,9 +374,9 @@ spec: ... ``` -Upon the creation of the configuration, the system will create a new -Revision, generating its name, and applying the spec and metadata from -the configuration, as well as new metadata labels: +Upon the creation of the configuration, the configuration controller +will create a new Revision, generating its name, and applying the spec +and metadata from the configuration, as well as new metadata labels: ```http GET /apis/elafros.dev/v1alpha1/namespaces/default/revisions/abc @@ -373,7 +439,7 @@ metadata: spec: ... # same as before status: - # the latest created and ready to serve. Watched by service + # the latest created and ready to serve. Watched by route latestReadyRevisionName: abc # latest created revision latestCreatedRevisionName: abc @@ -415,24 +481,46 @@ status: observedGeneration: 2145 ``` +The Service also watches the Configuration (and Route) and mirrors their status for convenience: + +```http +GET /apis/elafros.dev/v1alpha1/namespaces/default/services/my-service +``` +```yaml +apiVersion: elafros.dev/v1alpha1 +kind: Service +metadata: + name: my-service + generation: 1 + ... +spec: + ... # same as before +status: + # the latest created and ready to serve. + latestReadyRevisionName: abc + # latest created revision + latestCreatedRevisionName: abc + observedGeneration: 1 +``` + ## 3) Manual rollout of a new Revision - config change only -**_Scenario_**: User updates configuration with new configuration (env - var change) to an existing service, tests the revision, then +**_Scenario_**: User updates configuration with new runtime arguments + (env var change) to an existing service, tests the revision, then proceeds with a manually controlled rollout to 100% ``` -$ elafros rollout strategy manual +$ elafros rollout --service my-service strategy manual $ elafros deploy --service my-service --env HELLO="blurg" [...] $ elafros revisions list --service my-service -Name Traffic Id Date Deployer Git SHA -next 0% v3 2018-01-19 12:16 user1 a6f92d1 -current 100% v2 2018-01-18 20:34 user1 a6f92d1 - v1 2018-01-17 10:32 user1 33643fc +Name Traffic Id Date Deployer Git SHA +next 0% v3 2018-01-19 12:16 user1 a6f92d1 +current 100% v2 2018-01-18 20:34 user1 a6f92d1 + v1 2018-01-17 10:32 user1 33643fc $ elafros rollout next percent 5 [...] @@ -450,14 +538,11 @@ current,next 100% v3 2018-01-19 12:16 user1 a6f92d1 **Steps**: -* Update the Route to pin the current revision +* Update the Service to switch from `runLatest` to `pinned` strategy. -* Update the Configuration with the new configuration (env var) +* Update the Service with the new configuration (env var). -* Update the Route to address the new Revision - -* After testing the new revision through the named subdomain, proceed - with the rollout, incrementally increasing traffic to 100% +* Update the Service to address the new Revision. **Results:** @@ -465,21 +550,44 @@ current,next 100% v3 2018-01-19 12:16 user1 a6f92d1 addressable at next.my-service... (by convention), but traffic is not routed to it until the percentage is manually ramped up. Upon completing the rollout, the next revision is now the current - revision + revision. ![Manual rollout](images/manual_rollout.png) -In the previous examples, the route referenced a Configuration for -automatic rollouts of new Revisions. While this pattern is useful for -many scenarios such as functions-as-a-service and simple development -flows, the Route can also reference Revisions directly to "pin" -traffic to specific revisions, which is suitable for manually -controlling rollouts, i.e. testing a new revision prior to serving -traffic. (Note: see [Appendix B](complex_examples.md) for a -semi-automatic variation of manual rollouts). +In the previous examples, the Service automatically made changes to +the configuration (newly created Revision) routable when they became +ready. While this pattern is useful for many scenarios such as +functions-as-a-service and simple development flows, the Service can +also reference Revisions directly in `pinned` mode to route traffic to +a specific Revision, which is suitable for rolling back a service to a known-good state. manually controlling +rollouts, i.e. testing a new revision prior to serving traffic. (Note: +see [Appendix B](complex_examples.md) for a semi-automatic variation +of manual rollouts). -The client updates the route to pin the current revision: +The client updates the service to pin the current revision: + +```http +PUT /apis/elafros.dev/v1alpha1/namespaces/default/services/my-service +``` +```yaml +apiVersion: elafros.dev/v1alpha1 +kind: Service +metadata: + name: my-service +spec: + pinned: + revisionName: def + configuration: # Copied from spec.runLatest.configuration + revisionTemplate: # template for building Revision + spec: + container: + image: gcr.io/... # new image +``` + +This causes the Route to be updated to pin traffic the specified +revision (note that the Configuration between the two is equivalent, +and therefore unchanged). ```http PATCH /apis/elafros.dev/v1alpha1/namespaces/default/routes/my-service @@ -492,13 +600,39 @@ metadata: spec: rollout: traffic: - - revisionName: def # pin a specific revision, i.e. the current one + - revisionName: def + name: current # addressable as current.my-service.default.mydomain.com percent: 100 + - configurationName: my-service # LatestReadyRevision of my-service + name: next # addressable as next.my-service.default.mydomain.com + percent: 0 # no traffic yet +``` + +Next, the service is updated with the new variables, which causes the +service controller to update the Configuration, in this case updating +the environment but keeping the same container image: + +```http +PATCH /apis/elafros.dev/v1alpha1/namespaces/default/services/my-service +``` +```yaml +apiVersion: elafros.dev/v1alpha1 +kind: Service +metadata: + name: my-service +spec: + pinned: + configuration: # Copied from spec.runLatest.configuration + revisionTemplate: # template for building Revision + spec: + container: + env: # k8s-style strategic merge patch, updating a single list value + - name: HELLO + value: blurg # changed value ``` As in the previous example, the configuration is updated to trigger -the creation of a new revision, in this case updating the container -image but keeping the same config: +the creation of a new revision: ```http PATCH /apis/elafros.dev/v1alpha1/namespaces/default/configurations/my-service @@ -547,13 +681,13 @@ status: Even when ready, the new revision does not automatically start serving traffic, as the route was pinned to revision `def`. -Update the route to make the existing revision serving traffic -addressable through subdomain `current`, and referencing the new -revision at 0% traffic but making it addressable through subdomain -`next`: +Once the new revision is ready, the route will update the `next` name +to point to the revision `ghi`. The new revision will still not +receive any traffic by default, but can be accessed for testing, +verification, etc. ```http -PATCH /apis/elafros.dev/v1alpha1/namespaces/default/routes/my-service +GET /apis/elafros.dev/v1alpha1/namespaces/default/routes/my-service ``` ```yaml apiVersion: elafros.dev/v1alpha1 @@ -566,28 +700,9 @@ spec: - revisionName: def name: current # addressable as current.my-service.default.mydomain.com percent: 100 - - revisionName: ghi + - configurationName: my-service # LatestReadyRevision of my-service name: next # addressable as next.my-service.default.mydomain.com percent: 0 # no traffic yet -``` - -In this state, the route makes both revisions addressable with -subdomains `current` and `next` (once the revision `ghi` has a status of -Ready), but traffic has not shifted to next yet. Also note that while -the names current/next have semantic meaning, they are convention -only; blue/green, or any other subdomain names could be configured. - -```http -GET /apis/elafros.dev/v1alpha1/namespaces/default/routes/my-service -``` -```yaml -apiVersion: elafros.dev/v1alpha1 -kind: Route -metadata: - name: my-service - ... -spec: - ... # unchanged status: domain: my-service.default.mydomain.com traffic: @@ -600,68 +715,53 @@ status: conditions: - type: RolloutComplete status: True - ... ``` After testing the new revision at -`next.my-service.default.mydomain.com`, it can be rolled out to 100% -(either directly, or through several increments, with the split -totaling 100%): +`next.my-service.default.mydomain.com`, it can be promoted to live by +updating the service to pin `ghi` as the new revision. ```http -PATCH /apis/elafros.dev/v1alpha1/namespaces/default/routes/my-service +PATCH /apis/elafros.dev/v1alpha1/namespaces/default/services/my-service ``` ```yaml apiVersion: elafros.dev/v1alpha1 -kind: Route +kind: Service metadata: name: my-service spec: - rollout: - Traffic: # percentages must total 100% - - revisionName: def - name: current - percent: 0 - - revisionName: ghi - name: next - percent: 100 # migrate traffic fully to the next revision + pinned: + revisionname: ghi ``` -After reconciliation, all traffic has been shifted to the new version: +This causes the service to update the route to assign ```http -GET /apis/elafros.dev/v1alpha1/namespaces/default/routes/my-service +PATCH /apis/elafros.dev/v1alpha1/namespaces/default/routes/my-service ``` ```yaml apiVersion: elafros.dev/v1alpha1 -kind: Route +kind: route metadata: name: my-service - ... spec: - ... # unchanged -status: - domain: my-service.default.mydomain.com - traffic: - - revisionName: def - name: current - percent: 0 - - revisionName: ghi - name: next - percent: 100 - conditions: - - type: RolloutComplete - status: True - ... + rollout: + traffic: + - revisionName: ghi + name: current + percent: 100 + - configurationName: my-service # LatestReadyRevision of my-service + name: next + percent: 0 ``` -By convention, the final step when completing the rollout is to update -`current` to reflect the new revision. `next` can either be removed, or -left addressing the same revision as current so that +Once the update has been completed, if the latest ready revision is +the same as the pinned revision, the names `current` and `next` will +point to the same revision. Both names are left in place so that `next.my-service.default.mydomain.com` is always addressable. ```http -PATCH /apis/elafros.dev/v1alpha1/namespaces/default/routes/my-service +GET /apis/elafros.dev/v1alpha1/namespaces/default/routes/my-service ``` ```yaml apiVersion: elafros.dev/v1alpha1 @@ -677,6 +777,18 @@ spec: - revisionName: ghi # optional: leave next as also referring to ghi name: next percent: 0 +status: + domain: my-service.default.mydomain.com + traffic: + - revisionName: ghi + name: current # addressable as current.my-service.default.mydomain.com + percent: 100 + - revisionName: ghi + name: next # addressable as next.my-service.default.mydomain.com + percent: 0 + conditions: + - type: RolloutComplete + status: True ``` @@ -699,21 +811,22 @@ $ elafros deploy --service my-service **Steps**: -* Create/Update a Configuration, inlining build details. +* Create/Update the service, updating build source information and + using a new container label. **Results**: * The Configuration is created/updated, which generates a container - build and a new revision based on the template, and can be rolled + build and a new Revision based on the template, and can be rolled out per earlier examples ![Build Example](images/build_example.png) -Previous examples demonstrated configurations created with pre-built +Previous examples demonstrated services created with pre-built containers. Revisions can also be created by providing build -information to the configuration, which results in a container image -built by the system. The build information is supplied by inlining the +information to the service, which results in a container image built +by the system. The build information is supplied by inlining the BuildSpec of a Build resource in the Configuration. This describes: * **What** to build (`build.source`): Source can be provided as an @@ -727,47 +840,50 @@ BuildSpec of a Build resource in the Configuration. This describes: * **Where** to publish (`build.template.arguments`): Image registry url and other information specific to this build invocation. -The client creates the configuration inlining a build spec for an -archive based source build, and referencing a nodejs build template: +The client updates the configuration in the service inlining a build +spec for an git based source build, and referencing a nodejs build +template: ```http -POST /apis/elafros.dev/v1alpha1/namespaces/default/configurations +PATCH /apis/elafros.dev/v1alpha1/namespaces/default/service ``` ```yaml apiVersion: elafros.dev/v1alpha1 -kind: Configuration +kind: Service metadata: name: my-service spec: - build: # build.dev/v1alpha1.BuildTemplateSpec - source: - # oneof git|gcs|custom: - git: - url: https://... - commit: ... - template: # defines build template - name: nodejs_8_9_4 # builder name - namespace: build-templates - arguments: - - name: _IMAGE - value: gcr.io/... # destination for image - - revisionTemplate: # template for building Revision - metadata: ... - spec: - container: # k8s core.v1.Container - image: gcr.io/... # Promise of a future build. Same as supplied in - # build.template.arguments[_IMAGE] - env: # Updated environment variables to go live with new source. - - name: FOO - value: bar - - name: HELLO - value: world + runLatest: + configuration: + build: # elafros.dev/v1alpha1.BuildTemplateSpec + source: + # oneof git|gcs|custom: + git: + url: https://... + commit: ... + template: # defines build template + name: nodejs_8_9_4 # builder name + namespace: build-templates + arguments: + - name: _IMAGE + value: gcr.io/... # destination for image + + revisionTemplate: # template for building Revision + metadata: ... + spec: + container: # k8s core.v1.Container + image: gcr.io/... # Promise of a future build. Same as supplied in + # build.template.arguments[_IMAGE] + env: # Updated environment variables to go live with new source. + - name: FOO + value: bar + - name: HELLO + value: world ``` Note the `revisionTemplate.spec.container.image` above is supplied with the destination of the build. This enables one-step changes to -both config and source code. If the build step were responsible for +both environment and source code. If the build step were responsible for updating the `revisionTemplate.spec.container.image` at the completion of the build, an update to both source and config could result in the creation of two Revisions, one with the config change, and the other @@ -776,12 +892,14 @@ for the `buildName` to be complete and the `revisionTemplate.spec.container.image` to be live before marking the Revision as "ready". -Upon creating/updating the configuration's build field, the system -creates a new revision. The configuration controller will initiate a -build, populating the revision’s buildName with a reference to the -underlying Build resource. Via status updates which the revision -controller observes through the build reference, the high-level state -of the build is mirrored into conditions in the Revision’s status: +Upon creating/updating the service's configuration, the contents are +copied into the corresponding Configuration object. Once updated, the +configuration controller creates a new revision. The configuration +controller will also create a build, populating the revision’s +buildName with a reference to the underlying Build resource. The +revision controller watches status updates on the build reference, and +the high-level state of the build is mirrored into conditions in the +Revision’s status for convenience: ```http GET /apis/elafros.dev/v1alpha1/namespaces/default/revisions/abc @@ -797,7 +915,7 @@ metadata: elafros.dev/configurationGeneration: 1234 ... spec: - # name of the build.dev/v1alpha1.Build, if built from source. + # name of the elafros.dev/v1alpha1.Build, if built from source. # Set by Configuration. buildName: ... @@ -827,10 +945,12 @@ status: Rollout operations in the route are identical to the pre-built container examples. -Also analogous is updating the configuration to create a new -revision - in this case, updated source would be provided to the +Also analogous is creating the service from scratch with source +files - in this case, the source would be provided to the configuration's inlined build spec, which would initiate a new -container build, and the creation of a new revision. +container build, and the creation of a new revision. If the first +build fails `LatestReadyRevisionName` will be entirely unset until a +Revision is created which can become ready. ## 5) Deploy a Function @@ -851,7 +971,8 @@ $ elafros deploy --function index --service my-function **Steps**: -* Create/Update a Configuration, additionally specifying function details. +* Create/Update a service, specifying source code and function + details. **Results**: @@ -863,11 +984,12 @@ $ elafros deploy --function index --service my-function Previous examples illustrated creating and deploying revisions in the -context of apps. Functions are created and deployed in the same -manner (in particular, as containers which respond to HTTP). In the -build phase of the deployment, additional function metadata may be -taken into account in order to wrap the supplied code in a functions -framework. +context of application containers. Functions are created and deployed +in the same manner (in particular, as containers which respond to +HTTP). In the build phase of the deployment, additional function +metadata may be taken into account in order to wrap the supplied code +in a language-specific functions framework which translates from HTTP +to language-native constructs. Functions are configured with a language-specific entryPoint. The entryPoint may be provided as an argument to the build template, if @@ -880,53 +1002,56 @@ Note that a function may be connected to one or more event sources via Bindings in the Eventing API; the binding of events to functions is not a core function of the compute API. -Creating the configuration with build and function metadata: +Creating the service with build and function metadata: ```http -POST /apis/elafros.dev/v1alpha1/namespaces/default/configurations +POST /apis/elafros.dev/v1alpha1/namespaces/default/services ``` ```yaml apiVersion: elafros.dev/v1alpha1 -kind: Configuration +kind: Service metadata: name: my-function spec: - build: # build.dev/v1alpha1.BuildTemplateSpec - source: - # oneof git|gcs|custom - git: - url: https://... - commit: ... - template: # defines build template - name: go_1_9_fn # function builder - namespace: build-templates - arguments: - - name: _IMAGE - value: gcr.io/... # destination for image - - name: _ENTRY_POINT - value: index # language dependent, function-only entrypoint - - revisionTemplate: # template for building Revision - metadata: - labels: - # One-of "function" or "app", convention for CLI/UI clients to list/select - elafros.dev/type: "function" - spec: - container: # k8s core.v1.Container - image: gcr.io/... # Promise of a future build. Same as supplied in - # build.template.arguments[_IMAGE] - env: - - name: FOO - value: bar - - name: HELLO - value: world - - # serializes requests for function. Default value for functions - concurrencyModel: SingleThreaded - # max time allowed to respond to request - timeoutSeconds: 20 + runLatest: + configuration: + build: # elafros.dev/v1alpha1.BuildTemplateSpec + source: + # oneof git|gcs|custom + git: + url: https://... + commit: ... + template: # defines build template + name: go_1_9_fn # function builder + namespace: build-templates + arguments: + - name: _IMAGE + value: gcr.io/... # destination for image + - name: _ENTRY_POINT + value: index # language dependent, function-only entrypoint + + revisionTemplate: # template for building Revision + metadata: + labels: + # One-of "function" or "app", convention for CLI/UI clients to list/select + elafros.dev/type: "function" + spec: + container: # k8s core.v1.Container + image: gcr.io/... # Promise of a future build. Same as supplied in + # build.template.arguments[_IMAGE] + env: + - name: FOO + value: bar + - name: HELLO + value: world + + # serializes requests for function. Default value for functions + concurrencyModel: SingleThreaded + # max time allowed to respond to request + timeoutSeconds: 20 ``` -Upon creating or updating the configuration, a new Revision is created -per the previous examples. Rollout operations are also identical to -the previous examples. +Upon creating or updating the service, values are copied to the +configuration, which causes a new Revision to be created per the +previous examples. Rollout operations are also identical to the +previous examples. diff --git a/docs/spec/overview.md b/docs/spec/overview.md index edc05cd9ea9f..1fd68351378f 100644 --- a/docs/spec/overview.md +++ b/docs/spec/overview.md @@ -1,6 +1,7 @@ # Resource Types -The primary resources in the Elafros API are Routes, Revisions, and Configurations: +The primary resources in the Elafros API are Routes, Revisions, +Configurations, and Services: * A **Route** provides a named endpoint and a mechanism for routing traffic to @@ -8,6 +9,9 @@ The primary resources in the Elafros API are Routes, Revisions, and Configuratio * **Configuration**, which acts as a stream of environments for Revisions. +* **Service** acts as a top-level container for managing the set of + Routes and Configurations which implement a network service. + ![Object model](images/object_model.png) ## Route @@ -56,6 +60,17 @@ Configuration's controller will track the status of created Revisions and makes both the most recently created and most recently *ready* (i.e. healthy) Revision available in the status section. +## Service + +A **Service** encapsulates a set of **Routes** and **Configurations** +which together provide a software component. Service exists to provide +a singular abstraction which can be access controlled, reasoned about, +and which encapsulates software lifecycle decisions such as rollout +policy and team resource ownership. Service acts only as an +orchestrator of the underlying Routes and Configurations (much as a +kubernetes Deployment orchestrates ReplicaSets), and its usage is +optional but recommended. + # Orchestration @@ -73,9 +88,10 @@ is created or updated. This provides: in optimistic concurrency errors * the ability to rollback to a known good configuration -In the conventional single live revision scenario, a route has a -single configuration with the same name as the route. Update -operations on the configuration enable scenarios such as: +In the conventional single live revision scenario, a service creates +both a route and a configuration with the same name as the +service. Update operations on the service enable scenarios such +as: * *"Push code, keep config":* Specifying a new revision with updated source, inheriting configuration such as env vars from the @@ -83,10 +99,12 @@ operations on the configuration enable scenarios such as: * *"Update config, keep code"*: Specifying a new revision as just a change to configuration, such as updating an env variable, inheriting all other configuration and source/image. - -When creating an initial route and performing the first deployment, -the two operations of creating a Route and an associated Configuration -can be done in parallel, which streamlines the use case of deploying -code initially from a button. The -[sample API usage](normative_examples.md) section illustrates -conventional usage of the API. +* *"Execute a manual rollout"*: Updating the service when in pinned + rollout mode allows manual testing of a revision before making it + live. + +Using a Service object to orchestrate the creation a both route and +configuration allows deployment of code (e.g. from a github button) to +avoid needing to reason about sequencing and failure modes of parallel +resource creation. The [sample API usage](normative_examples.md) +section illustrates conventional usage of the API. diff --git a/docs/spec/spec.md b/docs/spec/spec.md index 0373b527ed4c..fe2e2cdc5600 100644 --- a/docs/spec/spec.md +++ b/docs/spec/spec.md @@ -119,7 +119,7 @@ metadata: ... spec: # +optional. composable Build spec, if omitted provide image directly - build: # This is a build.dev/v1alpha1.BuildTemplateSpec + build: # This is a elafros.dev/v1alpha1.BuildTemplateSpec source: # oneof git|gcs|custom: @@ -188,7 +188,7 @@ spec: serviceAccountName: ... # Name of the service account the code should run as. status: - # the latest created and ready to serve. Watched by route + # the latest created and ready to serve. Watched by Route latestReadyRevisionName: abc # latest created revision, may still be in the process of being materialized latestCreatedRevisionName: def @@ -227,7 +227,7 @@ metadata: # spec populated by Configuration spec: - # +optional. name of the build.dev/v1alpha1.Build if built from source + # +optional. name of the elafros.dev/v1alpha1.Build if built from source buildName: ... container: # corev1.Container @@ -286,3 +286,94 @@ status: ``` +## Service + +For a high-level description of Services, +[see the overview](overview.md#service). + + +```yaml +apiVersion: elafros.dev/v1alpha1 +kind: : +metadata: + name: myservice + namespace: default + labels: + elafros.dev/type: "function" # convention, one of "function" or "app" + # system generated meta + uid: ... +  resourceVersion: ... # used for optimistic concurrency control +  creationTimestamp: ... +  generation: ... +  selfLink: ... + ... + +# spec contains one of several possible rollout styles +spec: # One of "runLatest" or "pinned" + # Example, only one of runLatest or pinned can be set in practice. + runLatest: + configuration: # elafros.dev/v1alpha1.Configuration + # +optional. name of the elafros.dev/v1alpha1.Build if built from source + buildName: ... + + container: # core.v1.Container + image: gcr.io/... + command: ['run'] + args: [] + env: # list of environment vars + - name: FOO + value: bar + - name: HELLO + value: world + - ... + livenessProbe: ... # Optional + readinessProbe: ... # Optional + concurrencyModel: ... + timeoutSeconds: ... + serviceAccountName: ... # Name of the service account the code should run as + # Example, only one of runLatest or pinned can be set in practice. + pinned: + revisionName: myservice-00013 # Auto-generated revision name + configuration: # elafros.dev/v1alpha1.Configuration + # +optional. name of the elafros.dev/v1alpha1.Build if built from source + buildName: ... + + container: # core.v1.Container + image: gcr.io/... + command: ['run'] + args: [] + env: # list of environment vars + - name: FOO + value: bar + - name: HELLO + value: world + - ... + livenessProbe: ... # Optional + readinessProbe: ... # Optional + concurrencyModel: ... + timeoutSeconds: ... + serviceAccountName: ... # Name of the service account the code should run as +status: + # This information is copied from the owned Configuration and Route. + + # The latest created and ready to serve Revision. + latestReadyRevisionName: abc + # Latest created Revision, may still be in the process of being materialized. + latestCreatedRevisionName: def + + # domain: The hostname used to access the default (traffic-split) + # route. Typically, this will be composed of the name and namespace + # along with a cluster-specific prefix (here, mydomain.com). + domain: my-service.default.mydomain.com + + conditions: # See also the documentation in errors.md + - type: Ready + status: True + message: "Revision starting" + - type: LatestRevisionReady + status: False + reason: ContainerMissing + message: "Unable to start because container is missing and build failed." + + observedGeneration: ... # last generation being reconciled +```