Skip to content

Commit e80fd42

Browse files
committed
add UpdatePolicy to config
1 parent 656f52c commit e80fd42

File tree

7 files changed

+165
-27
lines changed

7 files changed

+165
-27
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ example:
22
go build -mod vendor
33
(cd example && \
44
INPUT_CREDS=.google_application_credentials.json \
5-
GITHUB_RUN_NUMBER=140 \
5+
GITHUB_RUN_NUMBER=170 \
66
GITHUB_SHA=13e82dd30df4e87118faa98712a5aebb0ab05c45 \
77
../gce-deploy-action)
88

README.md

Lines changed: 25 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -35,29 +35,37 @@ deploys:
3535
github-sha: $GITHUB_SHA
3636
tags:
3737
- my-tag123
38+
update_policy:
39+
min_ready_sec: 30
3840

3941
delete_instance_templates_after: false
4042
```
4143
4244
### Config Reference
4345
44-
| Variable | Description |
45-
|--------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
46-
| `deploys.*.name` | ***Required*** Name of the deploy |
47-
| `deploys.*.project` | Name of the Google Cloud project |
48-
| `deploys.*.creds` | Either a path or the contents of a Service Account JSON Key. Required, if not specified in Github action. |
49-
| `deploys.*.region` | ***Required*** Region of the instance group. |
50-
| `deploys.*.instance_group` | ***Required*** Name of the instance group. |
51-
| `deploys.*.instance_template_base` | ***Required*** Instance template to be used as base. |
52-
| `deploys.*.instance_template` | ***Required*** Name of the newly created instance template. |
53-
| `deploys.*.startup_script` | Path to script to run when VM boots. [Read more](https://cloud.google.com/compute/docs/startupscript) |
54-
| `deploys.*.shutdown_script` | Path to script to run when VM shuts down. [Read more](https://cloud.google.com/compute/docs/shutdownscript) |
55-
| `deploys.*.cloud_init` | Path to cloud-init file. [Read more](https://cloud.google.com/container-optimized-os/docs/how-to/create-configure-instance#using_cloud-init) |
56-
| `deploys.*.labels` | A set of key/value label pairs to assign to instances. |
57-
| `deploys.*.metadata` | A set of key/value metadata pairs to make available from within instances. |
58-
| `deploys.*.tags` | A list of tags to assign to instances. |
59-
| `deploys.*.vars` | A set of additional key/value variables which will be available in either startup_script, shutdown_script or cloud_init. They take precedence over ENV vars. |
60-
| `delete_instance_templates_after` | Delete old instance templates after duration, default '336h' (14 days). Set to 'false' to disable. |
46+
| Variable | Description |
47+
|--------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
48+
| `deploys.*.name` | ***Required*** Name of the deploy |
49+
| `deploys.*.project` | Name of the Google Cloud project |
50+
| `deploys.*.creds` | Either a path or the contents of a Service Account JSON Key. Required, if not specified in Github action. |
51+
| `deploys.*.region` | ***Required*** Region of the instance group. |
52+
| `deploys.*.instance_group` | ***Required*** Name of the instance group. |
53+
| `deploys.*.instance_template_base` | ***Required*** Instance template to be used as base. |
54+
| `deploys.*.instance_template` | ***Required*** Name of the newly created instance template. |
55+
| `deploys.*.startup_script` | Path to script to run when VM boots. [Read more](https://cloud.google.com/compute/docs/startupscript) |
56+
| `deploys.*.shutdown_script` | Path to script to run when VM shuts down. [Read more](https://cloud.google.com/compute/docs/shutdownscript) |
57+
| `deploys.*.cloud_init` | Path to cloud-init file. [Read more](https://cloud.google.com/container-optimized-os/docs/how-to/create-configure-instance#using_cloud-init) |
58+
| `deploys.*.labels` | A set of key/value label pairs to assign to instances. |
59+
| `deploys.*.metadata` | A set of key/value metadata pairs to make available from within instances. |
60+
| `deploys.*.tags` | A list of tags to assign to instances. |
61+
| `deploys.*.vars` | A set of additional key/value variables which will be available in either startup_script, shutdown_script or cloud_init. They take precedence over ENV vars. |
62+
| `deploys.*.update_policy.type` | The type of update process, must be either `PROACTIVE` (default) or `OPPORTUNISTIC`. [Read more](https://cloud.google.com/compute/docs/instance-groups/rolling-out-updates-to-managed-instance-groups#starting_an_opportunistic_or_proactive_update) |
63+
| `deploys.*.update_policy.replacement_method` | What action should be used to replace instances, must be either `SUBSTITUTE` (default) or `RECREATE`. [Read more](https://cloud.google.com/compute/docs/instance-groups/rolling-out-updates-to-managed-instance-groups#replacement_method) |
64+
| `deploys.*.update_policy.minimal_action` | Minimal action to be taken on an instance, possible values are `NONE`, `REFRESH`, `REPLACE` (default) or `RESTART`. [Read more](https://cloud.google.com/compute/docs/instance-groups/rolling-out-updates-to-managed-instance-groups#minimal_action) |
65+
| `deploys.*.update_policy.min_ready_sec` | Time to wait between consecutive instance updates, default is 10 seconds. [Read more](https://cloud.google.com/compute/docs/instance-groups/updating-managed-instance-groups#minimum_wait_time) |
66+
| `deploys.*.update_policy.max_surge` | Maximum number (or percentage, i.e. `15%`) of temporary instances to add while updating. Default is 3. [Read more](https://cloud.google.com/compute/docs/instance-groups/updating-managed-instance-groups#max_surge) |
67+
| `deploys.*.update_policy.max_unavailable` | Maximum number (or percentage, i.e. `100%`) of instances that can be offline at the same time while updating. Default is 0. [Read more](https://cloud.google.com/compute/docs/instance-groups/updating-managed-instance-groups#max_unavailable) |
68+
| `delete_instance_templates_after` | Delete old instance templates after duration, defaults to `336h` (14 days). Set to `false` to disable. |
6169

6270

6371
### Variables

config.go

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,21 @@ type Deploy struct {
8787
Labels map[string]string `yaml:"labels"`
8888
Metadata map[string]string `yaml:"metadata"`
8989
Tags []string `yaml:"tags"`
90+
UpdatePolicy UpdatePolicy `yaml:"update_policy"`
91+
}
92+
93+
type UpdatePolicy struct {
94+
Type string `yaml:"type"`
95+
ReplacementMethod string `yaml:"replacement_method"`
96+
MinimalAction string `yaml:"minimal_action"`
97+
MinReadySec string `yaml:"min_ready_sec"`
98+
minReadySec int
99+
MaxSurge string `yaml:"max_surge"`
100+
maxSurge int
101+
maxSurgeInPercent bool
102+
MaxUnavailable string `yaml:"max_unavailable"`
103+
maxUnavailable int
104+
maxUnavailableInPercent bool
90105
}
91106

92107
func ParseConfig(b io.Reader) (*Config, error) {
@@ -173,6 +188,77 @@ func ParseConfig(b io.Reader) (*Config, error) {
173188
for j := range dy.Tags {
174189
dy.Tags[j] = expandShellRe(dy.Tags[j], getEnv(nil))
175190
}
191+
192+
// expand vars in update policy
193+
dy.UpdatePolicy.Type = expandShellRe(dy.UpdatePolicy.Type, getEnv(nil))
194+
dy.UpdatePolicy.MinimalAction = expandShellRe(dy.UpdatePolicy.MinimalAction, getEnv(nil))
195+
dy.UpdatePolicy.ReplacementMethod = expandShellRe(dy.UpdatePolicy.ReplacementMethod, getEnv(nil))
196+
dy.UpdatePolicy.MinReadySec = expandShellRe(dy.UpdatePolicy.MinReadySec, getEnv(nil))
197+
dy.UpdatePolicy.MaxSurge = expandShellRe(dy.UpdatePolicy.MaxSurge, getEnv(nil))
198+
dy.UpdatePolicy.MaxUnavailable = expandShellRe(dy.UpdatePolicy.MaxUnavailable, getEnv(nil))
199+
200+
if strings.TrimSpace(dy.UpdatePolicy.Type) == "" {
201+
dy.UpdatePolicy.Type = "PROACTIVE"
202+
}
203+
204+
if strings.TrimSpace(dy.UpdatePolicy.MinimalAction) == "" {
205+
dy.UpdatePolicy.MinimalAction = "REPLACE"
206+
}
207+
208+
if strings.TrimSpace(dy.UpdatePolicy.ReplacementMethod) == "" {
209+
dy.UpdatePolicy.ReplacementMethod = "SUBSTITUTE"
210+
}
211+
212+
// parse update policy vars
213+
if dy.UpdatePolicy.MinReadySec != "" {
214+
minReadySec, err := strconv.Atoi(dy.UpdatePolicy.MinReadySec)
215+
if err != nil {
216+
return nil, fmt.Errorf("update_policy.min_ready_sec: %v", err)
217+
}
218+
dy.UpdatePolicy.minReadySec = minReadySec
219+
} else {
220+
dy.UpdatePolicy.minReadySec = 10 // set default
221+
}
222+
223+
if dy.UpdatePolicy.MaxSurge != "" {
224+
dy.UpdatePolicy.MaxSurge = strings.TrimSpace(dy.UpdatePolicy.MaxSurge)
225+
if strings.HasSuffix(dy.UpdatePolicy.MaxSurge, "%") {
226+
maxSurge, err := strconv.Atoi(strings.TrimSuffix(dy.UpdatePolicy.MaxSurge, "%"))
227+
if err != nil {
228+
return nil, fmt.Errorf("update_policy.max_surge: %v", err)
229+
}
230+
dy.UpdatePolicy.maxSurge = maxSurge
231+
dy.UpdatePolicy.maxSurgeInPercent = true
232+
} else {
233+
maxSurge, err := strconv.Atoi(dy.UpdatePolicy.MaxSurge)
234+
if err != nil {
235+
return nil, fmt.Errorf("update_policy.max_surge: %v", err)
236+
}
237+
dy.UpdatePolicy.maxSurge = maxSurge
238+
}
239+
} else {
240+
dy.UpdatePolicy.maxSurge = 3 // set default
241+
}
242+
243+
if dy.UpdatePolicy.MaxUnavailable != "" {
244+
dy.UpdatePolicy.MaxUnavailable = strings.TrimSpace(dy.UpdatePolicy.MaxUnavailable)
245+
if strings.HasSuffix(dy.UpdatePolicy.MaxUnavailable, "%") {
246+
maxUnavailable, err := strconv.Atoi(strings.TrimSuffix(dy.UpdatePolicy.MaxUnavailable, "%"))
247+
if err != nil {
248+
return nil, fmt.Errorf("update_policy.max_unavailable: %v", err)
249+
}
250+
dy.UpdatePolicy.maxUnavailable = maxUnavailable
251+
dy.UpdatePolicy.maxUnavailableInPercent = true
252+
} else {
253+
maxUnavailable, err := strconv.Atoi(dy.UpdatePolicy.MaxUnavailable)
254+
if err != nil {
255+
return nil, fmt.Errorf("update_policy.max_unavailable: %v", err)
256+
}
257+
dy.UpdatePolicy.maxUnavailable = maxUnavailable
258+
}
259+
} else {
260+
dy.UpdatePolicy.maxUnavailable = 0 // set default
261+
}
176262
}
177263

178264
// read contents of scripts and expand env vars

config_test.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,19 @@ deploys:
4040
metadatakey: metadatavalue-$BAR-${BAR}
4141
tags:
4242
- tagvalue-$BAR-${BAR}
43+
update_policy:
44+
type: type-$BAR-${BAR}
45+
minimal_action: minimal-action-$BAR-${BAR}
46+
replacement_method: replacement-method-$BAR-${BAR}
47+
min_ready_sec: $MIN_READY_SEC
48+
max_surge: $MAX_SURGE
49+
max_unavailable: $MAX_UNAVAILABLE
4350
`
4451

4552
environ = append(environ, "BAR=FOO")
53+
environ = append(environ, "MIN_READY_SEC=2")
54+
environ = append(environ, "MAX_SURGE=15%")
55+
environ = append(environ, "MAX_UNAVAILABLE=14")
4656
c, err := ParseConfig(strings.NewReader(config))
4757
require.NoError(t, err)
4858

@@ -72,6 +82,18 @@ deploys:
7282
assert.Equal(t, "labelvalue-FOO-FOO", c.Deploys[0].Labels["labelkey"])
7383
assert.Equal(t, "metadatavalue-FOO-FOO", c.Deploys[0].Metadata["metadatakey"])
7484
assert.Equal(t, "tagvalue-FOO-FOO", c.Deploys[0].Tags[0])
85+
86+
assert.Equal(t, "type-FOO-FOO", c.Deploys[0].UpdatePolicy.Type)
87+
assert.Equal(t, "minimal-action-FOO-FOO", c.Deploys[0].UpdatePolicy.MinimalAction)
88+
assert.Equal(t, "replacement-method-FOO-FOO", c.Deploys[0].UpdatePolicy.ReplacementMethod)
89+
assert.Equal(t, "2", c.Deploys[0].UpdatePolicy.MinReadySec)
90+
assert.Equal(t, 2, c.Deploys[0].UpdatePolicy.minReadySec)
91+
assert.Equal(t, "15%", c.Deploys[0].UpdatePolicy.MaxSurge)
92+
assert.Equal(t, 15, c.Deploys[0].UpdatePolicy.maxSurge)
93+
assert.Equal(t, true, c.Deploys[0].UpdatePolicy.maxSurgeInPercent)
94+
assert.Equal(t, "14", c.Deploys[0].UpdatePolicy.MaxUnavailable)
95+
assert.Equal(t, 14, c.Deploys[0].UpdatePolicy.maxUnavailable)
96+
assert.Equal(t, false, c.Deploys[0].UpdatePolicy.maxUnavailableInPercent)
7597
}
7698

7799
func TestExpandShellRe(t *testing.T) {

deploy.go

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package main
22

33
import (
4+
"fmt"
45
"net/http"
56

67
computeBeta "google.golang.org/api/compute/v0.beta"
@@ -55,13 +56,23 @@ func Run(githubActionConfig *GithubActionConfig, config *Config, deploy Deploy)
5556

5657
Infof("%v: Created new instance template '%v/%v'", deploy.Name, deploy.Project, deploy.InstanceTemplate)
5758

59+
maxSurge := fmt.Sprintf("%v", deploy.UpdatePolicy.maxSurge)
60+
if deploy.UpdatePolicy.maxSurgeInPercent {
61+
maxSurge += "%"
62+
}
63+
maxUnavailable := fmt.Sprintf("%v", deploy.UpdatePolicy.maxUnavailable)
64+
if deploy.UpdatePolicy.maxUnavailableInPercent {
65+
maxUnavailable += "%"
66+
}
67+
68+
Infof("%v: Started rolling deploy for instance group '%v/%v' with Update Type: %v, Minimal Action: %v, Replacement Method: %v, Min Ready: %vsec, Max Surge: %v, Max Unavailable: %v",
69+
deploy.Name, deploy.Project, deploy.InstanceGroup, deploy.UpdatePolicy.Type, deploy.UpdatePolicy.MinimalAction, deploy.UpdatePolicy.ReplacementMethod, deploy.UpdatePolicy.minReadySec, maxSurge, maxUnavailable)
70+
5871
// start rolling update via instance group manager
5972
if err := StartRollingUpdate(computeBetaService, deploy, instanceTemplateURL); err != nil {
6073
return err
6174
}
6275

63-
Infof("%v: Started rolling deploy for instance group '%v/%v'", deploy.Name, deploy.Project, deploy.InstanceGroup)
64-
6576
if config.deleteInstanceTemplatesAfter > 0 {
6677
if err := CleanupInstanceTemplates(computeService, deploy.Project, config.deleteInstanceTemplatesAfter); err != nil {
6778
LogWarning(err.Error(), map[string]string{"project": deploy.Project})

example/deploy.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,6 @@ deploys:
1515
- app123
1616
metadata:
1717
github_run_number: $GITHUB_RUN_NUMBER
18+
update_policy:
19+
min_ready_sec: 20
1820

0 commit comments

Comments
 (0)