diff --git a/.changelog/5787.txt b/.changelog/5787.txt new file mode 100644 index 00000000000..23758bd2755 --- /dev/null +++ b/.changelog/5787.txt @@ -0,0 +1,3 @@ +```release-note:enhancement +composer: Added support for `composer-1` and `composer-2` aliases in image version argument +``` diff --git a/google/resource_composer_environment.go b/google/resource_composer_environment.go index 0d22a35b79e..84f5b2c24bb 100644 --- a/google/resource_composer_environment.go +++ b/google/resource_composer_environment.go @@ -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{}{ @@ -322,7 +322,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, @@ -1903,48 +1903,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) { diff --git a/google/resource_composer_environment_test.go b/google/resource_composer_environment_test.go index ebb75d0091c..9533f67c44e 100644 --- a/google/resource_composer_environment_test.go +++ b/google/resource_composer_environment_test.go @@ -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 { diff --git a/website/docs/d/composer_image_versions.html.markdown b/website/docs/d/composer_image_versions.html.markdown index ac07c9d8125..b4d33987829 100644 --- a/website/docs/d/composer_image_versions.html.markdown +++ b/website/docs/d/composer_image_versions.html.markdown @@ -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 diff --git a/website/docs/r/composer_environment.html.markdown b/website/docs/r/composer_environment.html.markdown index 5288d3f52b8..b3ec0e88844 100644 --- a/website/docs/r/composer_environment.html.markdown +++ b/website/docs/r/composer_environment.html.markdown @@ -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" } } } @@ -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 { @@ -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). @@ -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. @@ -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}} -``` \ No newline at end of file +```