Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for aliases like composer-2 in Composer image version field #4131

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions .changelog/5787.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:enhancement
composer: Added support for `composer-1` and `composer-2` aliases in image version argument
```
77 changes: 52 additions & 25 deletions google-beta/resource_composer_environment.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import (
const (
composerEnvironmentEnvVariablesRegexp = "[a-zA-Z_][a-zA-Z0-9_]*."
composerEnvironmentReservedAirflowEnvVarRegexp = "AIRFLOW__[A-Z0-9_]+__[A-Z0-9_]+"
composerEnvironmentVersionRegexp = `composer-([0-9]+\.[0-9]+\.[0-9]+(-preview\.[0-9]+)?|latest)-airflow-([0-9]+\.[0-9]+(\.[0-9]+.*)?)`
composerEnvironmentVersionRegexp = `composer-(([0-9]+)(\.[0-9]+\.[0-9]+(-preview\.[0-9]+)?)?|latest)-airflow-(([0-9]+\.[0-9]+)(\.[0-9]+)?)`
)

var composerEnvironmentReservedEnvVar = map[string]struct{}{
Expand Down Expand Up @@ -354,7 +354,7 @@ func resourceComposerEnvironment() *schema.Resource {
AtLeastOneOf: composerSoftwareConfigKeys,
ValidateFunc: validateRegexp(composerEnvironmentVersionRegexp),
DiffSuppressFunc: composerImageVersionDiffSuppress,
Description: `The version of the software running in the environment. This encapsulates both the version of Cloud Composer functionality and the version of Apache Airflow. It must match the regular expression composer-[0-9]+\.[0-9]+(\.[0-9]+)?-airflow-[0-9]+\.[0-9]+(\.[0-9]+.*)?. The Cloud Composer portion of the version is a semantic version. The portion of the image version following 'airflow-' is an official Apache Airflow repository release name. See documentation for allowed release names.`,
Description: `The version of the software running in the environment. This encapsulates both the version of Cloud Composer functionality and the version of Apache Airflow. It must match the regular expression composer-([0-9]+(\.[0-9]+\.[0-9]+(-preview\.[0-9]+)?)?|latest)-airflow-([0-9]+\.[0-9]+(\.[0-9]+)?). The Cloud Composer portion of the image version is a full semantic version, or an alias in the form of major version number or 'latest'. The Apache Airflow portion of the image version is a full semantic version that points to one of the supported Apache Airflow versions, or an alias in the form of only major and minor versions specified. See documentation for more details and version list.`,
},
"python_version": {
Type: schema.TypeString,
Expand Down Expand Up @@ -2043,48 +2043,75 @@ func composerImageVersionDiffSuppress(_, old, new string, _ *schema.ResourceData
versionRe := regexp.MustCompile(composerEnvironmentVersionRegexp)
oldVersions := versionRe.FindStringSubmatch(old)
newVersions := versionRe.FindStringSubmatch(new)
if oldVersions == nil || len(oldVersions) < 4 {
if oldVersions == nil || len(oldVersions) < 8 {
// Somehow one of the versions didn't match the regexp or didn't
// have values in the capturing groups. In that case, fall back to
// an equality check.
if old != "" {
log.Printf("[WARN] Composer version didn't match regexp: %s", old)
log.Printf("[WARN] Image version didn't match regexp: %s", old)
}
return old == new
}
if newVersions == nil || len(newVersions) < 3 {
if newVersions == nil || len(newVersions) < 8 {
// Somehow one of the versions didn't match the regexp or didn't
// have values in the capturing groups. In that case, fall back to
// an equality check.
if new != "" {
log.Printf("[WARN] Composer version didn't match regexp: %s", new)
log.Printf("[WARN] Image version didn't match regexp: %s", new)
}
return old == new
}

// Check airflow version using the version package to account for
// diffs like 1.10 and 1.10.0
eq, err := versionsEqual(oldVersions[3], newVersions[3])
if err != nil {
log.Printf("[WARN] Could not parse airflow version, %s", err)
}
if !eq {
return false
oldAirflow := oldVersions[5]
oldAirflowMajorMinor := oldVersions[6]
newAirflow := newVersions[5]
newAirflowMajorMinor := newVersions[6]
// Check Airflow versions.
if oldAirflow == oldAirflowMajorMinor || newAirflow == newAirflowMajorMinor {
// If one of the Airflow versions specifies only major and minor version
// (like 1.10), we can only compare major and minor versions.
eq, err := versionsEqual(oldAirflowMajorMinor, newAirflowMajorMinor)
if err != nil {
log.Printf("[WARN] Could not parse airflow version, %s", err)
}
if !eq {
return false
}
} else {
// Otherwise, we compare the full Airflow versions (like 1.10.15).
eq, err := versionsEqual(oldAirflow, newAirflow)
if err != nil {
log.Printf("[WARN] Could not parse airflow version, %s", err)
}
if !eq {
return false
}
}

// Check composer version. Assume that "latest" means we should
// suppress the diff, because we don't have any other way of
// knowing what the latest version actually is.
if oldVersions[1] == "latest" || newVersions[1] == "latest" {
oldComposer := oldVersions[1]
oldComposerMajor := oldVersions[2]
newComposer := newVersions[1]
newComposerMajor := newVersions[2]
// Check Composer versions.
if oldComposer == "latest" || newComposer == "latest" {
// We don't know what the latest version is so we suppress the diff.
return true
} else if oldComposer == oldComposerMajor || newComposer == newComposerMajor {
// If one of the Composer versions specifies only major version
// (like 1), we can only compare major versions.
eq, err := versionsEqual(oldComposerMajor, newComposerMajor)
if err != nil {
log.Printf("[WARN] Could not parse composer version, %s", err)
}
return eq
} else {
// Otherwise, we compare the full Composer versions (like 1.18.1).
eq, err := versionsEqual(oldComposer, newComposer)
if err != nil {
log.Printf("[WARN] Could not parse composer version, %s", err)
}
return eq
}
// If neither version is "latest", check them using the version
// package like we did for airflow.
eq, err = versionsEqual(oldVersions[1], newVersions[1])
if err != nil {
log.Printf("[WARN] Could not parse composer version, %s", err)
}
return eq
}

func versionsEqual(old, new string) (bool, error) {
Expand Down
10 changes: 7 additions & 3 deletions google-beta/resource_composer_environment_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,15 @@ func TestComposerImageVersionDiffSuppress(t *testing.T) {
expected bool
}{
{"matches", "composer-1.4.0-airflow-1.10.0", "composer-1.4.0-airflow-1.10.0", true},
{"preview matches", "composer-1.17.0-preview.0-airflow-2.0.1", "composer-1.17.0-preview.0-airflow-2.0.1", true},
{"old latest", "composer-latest-airflow-1.10.0", "composer-1.4.1-airflow-1.10.0", true},
{"new latest", "composer-1.4.1-airflow-1.10.0", "composer-latest-airflow-1.10.0", true},
{"airflow equivalent", "composer-1.4.0-airflow-1.10.0", "composer-1.4.0-airflow-1.10", true},
{"airflow different", "composer-1.4.0-airflow-1.10.0", "composer-1.4-airflow-1.9.0", false},
{"preview matches", "composer-1.17.0-preview.0-airflow-2.0.1", "composer-1.17.0-preview.0-airflow-2.0.1", true},
{"composer alias equivalent", "composer-1.4.0-airflow-1.10.0", "composer-1-airflow-1.10", true},
{"composer alias different", "composer-1.4.0-airflow-2.1.4", "composer-2-airflow-2.2", false},
{"composer different", "composer-1.4.0-airflow-1.10.0", "composer-1.4.1-airflow-1.10.0", false},
{"airflow alias equivalent", "composer-1.4.0-airflow-1.10.0", "composer-1.4.0-airflow-1.10", true},
{"airflow alias different", "composer-1.4.0-airflow-2.1.4", "composer-1.4.0-airflow-2.2", false},
{"airflow different", "composer-1.4.0-airflow-1.10.0", "composer-1.4.0-airflow-1.9.0", false},
}

for _, tc := range cases {
Expand Down
2 changes: 1 addition & 1 deletion website/docs/d/composer_image_versions.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -43,5 +43,5 @@ The following arguments are supported:
The following attributes are exported:

* `image_versions` - A list of composer image versions available in the given project and location. Each `image_version` contains:
* `image_version_id` - The string identifier of the image version, in the form: "composer-x.y.z-airflow-a.b(.c)"
* `image_version_id` - The string identifier of the image version, in the form: "composer-x.y.z-airflow-a.b.c"
* `supported_python_versions` - Supported python versions for this image version
27 changes: 16 additions & 11 deletions website/docs/r/composer_environment.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ resource "google_composer_environment" "test" {

config {
software_config {
image_version = "composer-2.0.0-preview.3-airflow-2.1.2"
image_version = "composer-2-airflow-2.2.3"
}
}
}
Expand Down Expand Up @@ -157,7 +157,7 @@ resource "google_composer_environment" "test" {
config {

software_config {
image_version = "composer-2.0.0-preview.3-airflow-2.1.2"
image_version = "composer-2-airflow-2.2.3"
}

workloads_config {
Expand Down Expand Up @@ -451,9 +451,11 @@ The following arguments are supported:

The version of the software running in the environment. This encapsulates both the version of Cloud Composer
functionality and the version of Apache Airflow. It must match the regular expression
`composer-[0-9]+\.[0-9]+(\.[0-9]+)?-airflow-[0-9]+\.[0-9]+(\.[0-9]+.*)?`.
The Cloud Composer portion of the version is a semantic version.
The portion of the image version following 'airflow-' is an official Apache Airflow repository release name.
`composer-([0-9]+(\.[0-9]+\.[0-9]+(-preview\.[0-9]+)?)?|latest)-airflow-([0-9]+\.[0-9]+(\.[0-9]+)?)`.
The Cloud Composer portion of the image version is a full semantic version, or an alias in the form of major
version number or 'latest'.
The Apache Airflow portion of the image version is a full semantic version that points to one of the
supported Apache Airflow versions, or an alias in the form of only major and minor versions specified.
For more information about Cloud Composer images, see
[Cloud Composer version list](https://cloud.google.com/composer/docs/concepts/versioning/composer-versions).

Expand Down Expand Up @@ -763,11 +765,14 @@ The `software_config` block supports:

The version of the software running in the environment. This encapsulates both the version of Cloud Composer
functionality and the version of Apache Airflow. It must match the regular expression
`composer-[0-9]+\.[0-9]+(\.[0-9]+)?-airflow-[0-9]+\.[0-9]+(\.[0-9]+.*)?`.
The Cloud Composer portion of the version is a semantic version.
The portion of the image version following 'airflow-' is an official Apache Airflow repository release name.
**Important**: You can only upgrade in-place between minor Cloud Composer versions. For example, you can upgrade
your environment from `composer-1.16.x` to `composer-1.17.x`. You cannot upgrade between major Cloud Composer
`composer-([0-9]+(\.[0-9]+\.[0-9]+(-preview\.[0-9]+)?)?|latest)-airflow-([0-9]+\.[0-9]+(\.[0-9]+)?)`.
The Cloud Composer portion of the image version is a full semantic version, or an alias in the form of major
version number or 'latest'.
The Apache Airflow portion of the image version is a full semantic version that points to one of the
supported Apache Airflow versions, or an alias in the form of only major and minor versions specified.
**Important**: You can only upgrade in-place between minor or patch versions of Cloud Composer or Apache
Airflow. For example, you can upgrade your environment from `composer-1.16.x` to `composer-1.17.x`, or from
`airflow-2.1.x` to `airflow-2.2.x`. You cannot upgrade between major Cloud Composer or Apache Airflow
versions (from `1.x.x` to `2.x.x`). To do so, create a new environment.


Expand Down Expand Up @@ -963,4 +968,4 @@ Environment can be imported using any of these accepted formats:
$ terraform import google_composer_environment.default projects/{{project}}/locations/{{region}}/environments/{{name}}
$ terraform import google_composer_environment.default {{project}}/{{region}}/{{name}}
$ terraform import google_composer_environment.default {{name}}
```
```