| OLD | NEW |
| (Empty) |
| 1 // Copyright 2016 The LUCI Authors. All rights reserved. | |
| 2 // Use of this source code is governed under the Apache License, Version 2.0 | |
| 3 // that can be found in the LICENSE file. | |
| 4 | |
| 5 // Package main contains the entry point code for the LUCI Deployment Tool | |
| 6 // ("deploytool"). This tool is a command-line interface designed to perform | |
| 7 // controlled and automated deployment of LUCI (and LUCI-compatible) services. | |
| 8 // | |
| 9 // "deploytool" is Linux-oriented, but may also work on other platforms. It | |
| 10 // leverages external tooling for many remote operations; it is the | |
| 11 // responsibility of the user to have suitable versions of that tooling | |
| 12 // installed and available on PATH. | |
| 13 // | |
| 14 // Overview | |
| 15 // | |
| 16 // The LUCI Deployment Tool loads awareness of services from a | |
| 17 // Layout, which consists of a set of configuration files. These files define: | |
| 18 // - Source Groups, named collections of Sources, which are versioned | |
| 19 // repositories from which deployments are built. | |
| 20 // - Projects, which are collections of independent deployable Components. | |
| 21 // - Deployments, which bind a Project to a Source Group and quantify resou
rce | |
| 22 // and deployment layouts for those Projects. | |
| 23 // | |
| 24 // All configurations are defined in protobufs, specified at: | |
| 25 // https://github.com/luci/luci-go/tree/master/common/proto/deploy | |
| 26 // | |
| 27 // A layout may define multiple Deployments of the same Project (e.g., | |
| 28 // "production", "staging", "development"). Each of these may be bound to the | |
| 29 // same or different source groups as per your project's deployment methodology. | |
| 30 // | |
| 31 // When initialized, "deploytool" is pointed to a Layout. It loads all of the | |
| 32 // configuration files in this layout and composes a view of all deployable | |
| 33 // options, asserting the validity of the Layout in the process. | |
| 34 // | |
| 35 // The user specifies the deployments to process, and the tool proceeds to: | |
| 36 // - Check out the Source Groups referenced by those deployments. | |
| 37 // - This includes checking out all of the individual Sources that each | |
| 38 // Source Group is composed of. | |
| 39 // - Loading a Deployment's Project Components from the Source Group and | |
| 40 // Sources that the Deployment is bound to. | |
| 41 // | |
| 42 // A fully-initialized Deployment configuration consists of data loaded from the | |
| 43 // initialy Layout combined with data loaded from that Deployment's Sources. | |
| 44 // | |
| 45 // After Deployment configuration is initialized, the deployment process | |
| 46 // consists of the following stages: | |
| 47 // - Staging: Creating a filesystem space for each staging layout that | |
| 48 // hermetically mixes each Component's referenced Sources, derivative | |
| 49 // variables, and generated code in a layout conducive to the further | |
| 50 // deployment of that component. | |
| 51 // - Build: Any required local operations are performed on the staging layo
ut | |
| 52 // to prepare for deployment. | |
| 53 // - Push: Remote services are engaged, and the deployed components are | |
| 54 // registered and/or installed from the staging area. However, they are n
ot | |
| 55 // yet activated. | |
| 56 // - Commit: The Pushed Components and related configurations are activated | |
| 57 // in the remote configuration and the application is deployed. | |
| 58 // | |
| 59 // Layout | |
| 60 // | |
| 61 // The overall set of deployment options, configuration, and associations is | |
| 62 // defined in a central layout configuration. The layout consists of several | |
| 63 // files in sub-directories, each of which define various configuration | |
| 64 // parameters of the larger layout. A layout minimally includes a layout | |
| 65 // configuration file; there is a set of layout-relative paths that are used by | |
| 66 // default, though they can be overridden by the layout file. The default | |
| 67 // hierarchy is: | |
| 68 // /layout.cfg (Layout configuration file) | |
| 69 // | |
| 70 // /sources/<source-group> (Defines a source group named "source-group") | |
| 71 // /sources/<source-group>/<source>.cfg (Defines a source, "source" within | |
| 72 // "source-group") | |
| 73 // /sources/<source-group>/... (Additional Sources within "source-group") | |
| 74 // | |
| 75 // /projects/<project>.cfg (Defines a Project named "project") | |
| 76 // /projects/... (Additional Projects) | |
| 77 // | |
| 78 // /deployments/<deployment>.cfg (Defines a Deployment named "deployment") | |
| 79 // /deployments/... (Additional Deployments) | |
| 80 // | |
| 81 // Each of these files is a text-formatted protobuf, defined in the deployment | |
| 82 // protobuf package: | |
| 83 // https://github.com/luci/luci-go/tree/master/common/proto/deploy/config.proto | |
| 84 // | |
| 85 // <source>.cfg: "Source" protobuf. | |
| 86 // <project>.cfg: "Project" protobuf. | |
| 87 // <deployment>.cfg: "Deployment" protobuf. | |
| 88 // | |
| 89 // Each Project references a set of Components within specific Sources. Those | |
| 90 // Components are, themselves, defined by a text "Component" protobuf. | |
| 91 // | |
| 92 // Source Groups and Sources | |
| 93 // | |
| 94 // A Source Group is a named collection of Source entries. Each Source is, | |
| 95 // itself a named reference to a source repository. Sources are checked out, | |
| 96 // updated, and initialized during "deploytool"'s checkout phase. | |
| 97 // | |
| 98 // At a high level, Projects directly reference Source names (without their | |
| 99 // Source Group), and Deployments directly bind to Source Groups. A Project | |
| 100 // bound to a Deployment, then, uses the Source Group referenced by that | |
| 101 // Deployment and the Source referenced by that Project to arrive at the actual | |
| 102 // source repository that is being referenced. | |
| 103 // | |
| 104 // This allows a single Project to be built from different sources by several | |
| 105 // different Deployments (e.g., development, staging, and production). This is | |
| 106 // accomplished by having each of those Deployments reference a different Source | |
| 107 // Group, but having each of those Source Groups contain a Source definition | |
| 108 // for each Source referenced by the project. For example: | |
| 109 // /sources/production/myrepo.cfg | |
| 110 // /sources/staging/myrepo.cfg | |
| 111 // /sources/development/myrepo.cfg | |
| 112 // /projects/myproject.cfg | |
| 113 // /deployments/myproject-production.cfg | |
| 114 // /deployments/myproject-staging.cfg | |
| 115 // /deployments/myproject-development.cfg | |
| 116 // | |
| 117 // The "myproject.cfg" would reference a Source named "myrepo". Any given | |
| 118 // Deployment's projection of the Project would select a different Source, with | |
| 119 // "myproject-production" referencing "production/myrepo", "myproject-staging" | |
| 120 // referencing "staging/myrepo", and "myproject-development" referencing | |
| 121 // "development/myrepo". | |
| 122 // | |
| 123 // Source repositories may include a source initialization file in their root, | |
| 124 // called ".luci-deploytool.cfg". This file contains a text "SourceLayout" | |
| 125 // protobuf, and allows a source to self-describe and perform post-checkout | |
| 126 // initialization. | |
| 127 // | |
| 128 // NOTE: For security reasons, initialization scripts will not be permitted
to | |
| 129 // run unless the Source entry describing that repository has its | |
| 130 // "run_init_scripts" value set to "true". | |
| 131 // | |
| 132 // Staging | |
| 133 // | |
| 134 // A staging space is created for each deployable Component. Staging offers | |
| 135 // "deploytool" an isolated canvas with which it can the filesystem layouts for | |
| 136 // that Component's actual deployment tooling to operate. All file operations, | |
| 137 // generation, and structuring are performed in the staging state such that the | |
| 138 // resulting staging directory is available for tooling or humans to use. | |
| 139 // | |
| 140 // The staging space for a given Component depends on that Component's type. | |
| 141 // A staging layout is intended to be human-navigatable while conforming to any | |
| 142 // layout requirements imposed by that Component's deployment tooling. | |
| 143 // | |
| 144 // One goal that is enforced is that the actual checkout directories used by | |
| 145 // any given Component are considered read-only. This means that staging for | |
| 146 // Components which require generated files to exist alongside source must | |
| 147 // copy or mirror that source elsewhere. Some of the more convoluted aspects of | |
| 148 // staging layouts are the result of this requirement. | |
| 149 // | |
| 150 // Staging - AppEngine - Generic | |
| 151 // | |
| 152 // AppEngine deployments are composed of two sets of information: | |
| 153 // - Individual AppEngine modules, including the default module. | |
| 154 // - AppEngine Project-wide globals such as Index, Cron, Dispatch, and Queu
e | |
| 155 // settings. | |
| 156 // | |
| 157 // The individual Components are staged and deployed independently. The globals | |
| 158 // are composed of their respective settings in each individual Component | |
| 159 // associated with the AppEngine project regardless of whether that Component is | |
| 160 // actually being deployed. The aggregate globals are re-asserted once per | |
| 161 // cloud project at the end of module deployment. | |
| 162 // | |
| 163 // References to static content are flattened into static directories within the | |
| 164 // staging area. The generated YAML files are configured to point to this | |
| 165 // flattened space regardless of the original static content's location within | |
| 166 // the source. | |
| 167 // | |
| 168 // Staging - AppEngine - dev_appserver | |
| 169 // | |
| 170 // Moving Component configuration into protobufs and constructing composite | |
| 171 // GOPATH and generated configuration files prevents AppEngine tooling from | |
| 172 // working out of the box in the source repository. | |
| 173 // | |
| 174 // The offered solution is to manually stage the Components under test, then | |
| 175 // run tooling against the staged Component directories. The user may optionally | |
| 176 // install the staged paths (GOPATH, etc.) or use their default environment's | |
| 177 // paths. | |
| 178 // | |
| 179 // One downside to this solution is that staging operates on a snapshot of the | |
| 180 // repository, meaning that changes to the repository won't be reflected in the | |
| 181 // staged environment. The user can address this by either manually re-staging | |
| 182 // the Deployment when a file changes. The user may also structure their | |
| 183 // application such that the staged content doesn't change frequently (e.g., | |
| 184 // for Go, have a simple entry point that immediately imports the main app | |
| 185 // logic). Specific structures depend on the type of Component and how it is | |
| 186 // staged (see below). | |
| 187 // | |
| 188 // In the future, a command to bootstrap "dev_appserver" through the staged | |
| 189 // environment would be useful. | |
| 190 // | |
| 191 // Staging - AppEngine Classic - Go | |
| 192 // | |
| 193 // Go Classic AppEngine Components are deployed using the `appcfg.py` tool, | |
| 194 // which is the fastest available method to deploy such applications. | |
| 195 // | |
| 196 // Because the "app.yaml" file must exist alongside the deployed source, a | |
| 197 // stub entry point is generated in the staging area. This stub simply imports | |
| 198 // the actual entry point package. | |
| 199 // | |
| 200 // The Component's Sources which declare GOPATH presence are combined in a | |
| 201 // virutal GOPATH within the Component's staging area. This GOPATH is then | |
| 202 // installed and `appcfg.py`'s "update" method is invoked to upload the | |
| 203 // Component. | |
| 204 // | |
| 205 // Staging - AppEngine Managed VM - Go | |
| 206 // | |
| 207 // Go AppEngine Managed VMs use a set of tools for deployment: | |
| 208 // - `aedeploy`, an AppEngine project tool which copies the various referen
ced | |
| 209 // sources across GOPATH entries into a single GOPATH hierarchy for Docke
r | |
| 210 // isolation. | |
| 211 // - `gcloud`, which engages the remote AppEngine service and offers deploy
ment | |
| 212 // utility. | |
| 213 // - Behind the scenes, `gcloud` uses `docker` to build the actual deployed | |
| 214 // image from the source (`aedeploy` target) and assembled GOPATH. | |
| 215 // | |
| 216 // Because the "app.yaml" file must exist alongside the entry point code, the | |
| 217 // contents of the entry package are copied (via symlink) into a generated | |
| 218 // entry point package in the staging area. | |
| 219 // | |
| 220 // The Component's Sources which declare GOPATH presence are combined in a | |
| 221 // virutal GOPATH within the Component's staging area. This GOPATH is then | |
| 222 // collapsed by `aedeploy` when the Managed VM image is built. | |
| 223 // | |
| 224 // NOTE: because the entry point is actually a clone of the entry point | |
| 225 // package, "internal/" imports will not work. This can be easily worked ar
ound | |
| 226 // by having the entry point import another non-internal package within the | |
| 227 // project, and having that package act as the actual entry point. | |
| 228 // | |
| 229 // Staging - AppEngine - Static Content Module | |
| 230 // | |
| 231 // The "deploytool" supports the concept of a static content module. This is an | |
| 232 // AppEngine module whose sole purpose is to, via AppEngine handler definitions, | |
| 233 // map to uploaded static content. The utility of a static module is that it can | |
| 234 // be effortlessly updated without impacting actual AppEngine runtime processes. | |
| 235 // Static modules can be used in conjunction with module-referencing handlers in | |
| 236 // the default AppEngine module to create the effect of the default module | |
| 237 // actually hosting the static content. | |
| 238 // | |
| 239 // Static content modules are staged like other AppEngine modules, only with | |
| 240 // no running code. | |
| 241 // | |
| 242 // Staging - Container Engine - Generic | |
| 243 // | |
| 244 // The "deploytool" supports depoying services to Google Container Engine, which | |
| 245 // is backed by Kubernetes. A project's Container Engine configuration consists | |
| 246 // of a series of Container Engine clusters, each of which hosts a series of | |
| 247 // homogenous machines. Kubernetes Pods, each of which are composed of one or | |
| 248 // more Kubernetes Components (i.e., Docker images), are deployed to one or more | |
| 249 // Google Container Engine Clusters. | |
| 250 // | |
| 251 // The Deployment Component defines a series of Container Engine Pods, an | |
| 252 // amalgam of a Kubernetes Pod definition and Container Engine requirements of | |
| 253 // that Kubernetes Pod. Each Kubernetes Pod's Component is built in its own | |
| 254 // staging area and deployed as part of that Pod to one or more Container Engine | |
| 255 // clusters. | |
| 256 // | |
| 257 // The Build phase of Container Engine deployment constructs a local Docker | |
| 258 // image of each Kubernetes Component. The Push phase pushes those images to | |
| 259 // the remote Docker image service. The Commit phase enacts the generated | |
| 260 // Kubernetes/ configuration which references those images on the Container | |
| 261 // Engine configuration. | |
| 262 // | |
| 263 // Container Engine management is done in two layers: firstly, the Container | |
| 264 // Engine configuration is managed with `gcloud` commands. This configures: | |
| 265 // - Which managed Clusters exist on Google Container Engine. | |
| 266 // - What scopes, system specs, and node count each Cluster has. | |
| 267 // | |
| 268 // Within a Cluster, several managed Kubernetes pods are deployed. The | |
| 269 // deployment is done using the `kubectl` tool, selecting the cluster using | |
| 270 // the "--context" flag to select the `gcloud`-generated Kubernetes context. | |
| 271 // | |
| 272 // Each "deploytool" Component (Kubernetes Pod, at this level) is managed as a | |
| 273 // Kubernetes Deployment (http://kubernetes.io/docs/user-guide/deployments/). A | |
| 274 // "deploytool"-managed Deployment will have the following metadata annotations: | |
| 275 // - "luci.managedBy", set to "luci-deploytool" | |
| 276 // - "luci.deploytool/version" set to the "deploytool" Deployment's version | |
| 277 // string. | |
| 278 // - "luci.deploytool/sourceVersion" set to the revision of the Deployment | |
| 279 // Component's source. | |
| 280 // | |
| 281 // The Kubernetes Deployment for a Component will be named | |
| 282 // "<project>--<component>". | |
| 283 // | |
| 284 // "deploytool"-driven Kubernetes depoyment is fairly straightforward: | |
| 285 // - Use "kubectl get deployments/<name>" to get the current Deployment sta
te. | |
| 286 // - If there is a current Deployment, | |
| 287 // - If its "luci.managedBy" annotation doesn't equal "luci-deploytool", | |
| 288 // fail. The user must manually correct this situation. | |
| 289 // - Check if the "luci.deploytool/version" matches the container version
. | |
| 290 // If it does, succeed. | |
| 291 // - Create/update the Deployment's configuration using "kubectl apply". | |
| 292 // | |
| 293 // Staging - Container Engine Pod Components - Go | |
| 294 // | |
| 295 // Go Container Engine Pods use a set of tools for deployment: | |
| 296 // - `aedeploy`, an AppEngine project tool which copies the various referen
ced | |
| 297 // sources across GOPATH entries into a single GOPATH hierarchy for Docke
r | |
| 298 // isolation. | |
| 299 // - `gcloud`, which engages the remote AppEngine service and offers deploy
ment | |
| 300 // utility. | |
| 301 // - `docker`, which is used to build and manage the Docker images. | |
| 302 // | |
| 303 // The Component's Sources which declare GOPATH presence are combined in a | |
| 304 // virutal GOPATH within the Component's staging area. This GOPATH is then | |
| 305 // collapsed by `aedeploy` when the Docker image is built. | |
| 306 // | |
| 307 // The actual Component is built directly from the Source using `aedeploy` and | |
| 308 // `docker build`. This is acceptable, since this is a read-only operation and | |
| 309 // will not modify the Source. | |
| 310 // | |
| 311 // To Do | |
| 312 // | |
| 313 // Following are some ideas of "planned" features that would be useful to add | |
| 314 // to "deploytool": | |
| 315 // - Add a "manage" command to manage a specific Deployment and/or Componen
t. | |
| 316 // Each invocation would load special set of subcommands based on that | |
| 317 // resource: | |
| 318 // - If the resource is a Deployment, query status? | |
| 319 // - If the resource is a Kubernetes Component, offer: | |
| 320 // - "kubectl" fall-through, automatically specifying the right Kuberne
tes | |
| 321 // Context for the Component's Compute Engine Cluster. | |
| 322 // - Other common automatable management macros. | |
| 323 // - Offer a "dev_appserver" fallthrough to create a staging area and | |
| 324 // bootstrap"dev_appserver" for the named staged AppEngine Components. | |
| 325 package main | |
| OLD | NEW |