diff --git a/GoogleCloudExtension/GoogleCloudExtension.GCloud/Models/CloudSdkVersions.cs b/GoogleCloudExtension/GoogleCloudExtension.GCloud/Models/CloudSdkVersions.cs
index 176c7d8e0..e590bd1b2 100644
--- a/GoogleCloudExtension/GoogleCloudExtension.GCloud/Models/CloudSdkVersions.cs
+++ b/GoogleCloudExtension/GoogleCloudExtension.GCloud/Models/CloudSdkVersions.cs
@@ -13,6 +13,8 @@
// limitations under the License.
using Newtonsoft.Json;
+using Newtonsoft.Json.Converters;
+using System;
namespace GoogleCloudExtension.GCloud.Models
{
@@ -25,6 +27,7 @@ public class CloudSdkVersions
/// The version of the Cloud SDK itself.
///
[JsonProperty("Google Cloud SDK")]
- public string SdkVersion { get; set; }
+ [JsonConverter(typeof(VersionConverter))]
+ public Version SdkVersion { get; set; }
}
}
diff --git a/GoogleCloudExtension/GoogleCloudExtension/GCloud/GCloudContext.cs b/GoogleCloudExtension/GoogleCloudExtension/GCloud/GCloudContext.cs
index 7e23bc583..4a9b47ca5 100644
--- a/GoogleCloudExtension/GoogleCloudExtension/GCloud/GCloudContext.cs
+++ b/GoogleCloudExtension/GoogleCloudExtension/GCloud/GCloudContext.cs
@@ -13,6 +13,7 @@
// limitations under the License.
using GoogleCloudExtension.Accounts;
+using GoogleCloudExtension.GCloud.Models;
using GoogleCloudExtension.Utils;
using System;
using System.Collections.Generic;
@@ -26,27 +27,40 @@ namespace GoogleCloudExtension.GCloud
///
public class GCloudContext : IGCloudContext
{
+ ///
+ /// The first version of gcloud with the builds group.
+ ///
+ ///
+ public const string GCloudBuildsMinimumVersion = "207.0.0";
+
private const string GCloudMetricsVariable = "CLOUDSDK_METRICS_ENVIRONMENT";
private const string GCloudMetricsVersionVariable = "CLOUDSDK_METRICS_ENVIRONMENT_VERSION";
+ ///
+ /// The first version of gcloud with the builds group.
+ ///
+ ///
+ private static readonly Version s_gCloudBuildsMinimumVersion = new Version(GCloudBuildsMinimumVersion);
+
///
/// The path to the credentials .json file to use for the call. The .json file should be a
- /// format accetable by gcloud's --credential-file-override parameter. Typically an authorize_user kind.
+ /// format acceptable by gcloud's --credential-file-override parameter. Typically an authorize_user kind.
///
public string CredentialsPath { get; }
///
- /// The project id of the project to use for the invokation of gcloud.
+ /// The project id of the project to use for the invocation of gcloud.
///
public string ProjectId { get; }
protected readonly Dictionary Environment = new Dictionary
{
[GCloudMetricsVariable] = GoogleCloudExtensionPackage.Instance.ApplicationName,
- [GCloudMetricsVersionVariable] =
- GoogleCloudExtensionPackage.Instance.ApplicationVersion
+ [GCloudMetricsVersionVariable] = GoogleCloudExtensionPackage.Instance.ApplicationVersion
};
+ private readonly Task _versionsTask;
+
///
/// Creates the default GCloud context from the current environment.
///
@@ -54,6 +68,7 @@ public GCloudContext()
{
CredentialsPath = CredentialsStore.Default.CurrentAccountPath;
ProjectId = CredentialsStore.Default.CurrentProjectId;
+ _versionsTask = GetGcloudOutputAsync("version");
}
///
@@ -92,10 +107,13 @@ public Task DeployAppAsync(string appYaml, string version, bool promote, A
/// The name of the image to build.
/// The contents of the container, including the Dockerfile.
/// The action to perform on each line of output.
- public Task BuildContainerAsync(string imageTag, string contentsPath, Action outputAction)
+ public async Task BuildContainerAsync(string imageTag, string contentsPath, Action outputAction)
{
- string command = $"container builds submit --tag=\"{imageTag}\" \"{contentsPath}\"";
- return RunGcloudCommandAsync(command, outputAction);
+ CloudSdkVersions sdkVersions = await _versionsTask;
+ string group = sdkVersions.SdkVersion >= s_gCloudBuildsMinimumVersion ? "builds" : "container builds";
+
+ string command = $"{group} submit --tag=\"{imageTag}\" \"{contentsPath}\"";
+ return await RunGcloudCommandAsync(command, outputAction);
}
///
diff --git a/GoogleCloudExtension/GoogleCloudExtension/GCloud/GCloudWrapper.cs b/GoogleCloudExtension/GoogleCloudExtension/GCloud/GCloudWrapper.cs
index 624d44d36..ad4058d7f 100644
--- a/GoogleCloudExtension/GoogleCloudExtension/GCloud/GCloudWrapper.cs
+++ b/GoogleCloudExtension/GoogleCloudExtension/GCloud/GCloudWrapper.cs
@@ -45,7 +45,7 @@ public class GCloudWrapper : IGCloudWrapper
new Dictionary
{
[GCloudComponent.Beta] = "beta",
- [GCloudComponent.Kubectl] = "kubectl",
+ [GCloudComponent.Kubectl] = "kubectl"
};
private readonly Lazy _processService;
@@ -89,7 +89,7 @@ public async Task ValidateGCloudAsync(GCloudComponent co
/// in . If the does not refer to a supported CVS (currently git) then
/// nothing will be done.
///
- /// The directory for which to generate the source contenxt.
+ /// The directory for which to generate the source context.
/// Where to store the source context files.
/// The task to be completed when the operation finishes.
public async Task GenerateSourceContextAsync(string sourcePath, string outputPath)
@@ -109,7 +109,7 @@ private async Task> GetInstalledComponentsAsync()
return components.Where(x => x.State.IsInstalled).Select(x => x.Id).ToList();
}
- private bool IsGCloudCliInstalled()
+ private static bool IsGCloudCliInstalled()
{
Debug.WriteLine("Validating GCloud installation.");
string gcloudPath = PathUtils.GetCommandPathFromPATH("gcloud.cmd");
@@ -136,7 +136,7 @@ private async Task GetInstalledCloudSdkVersionAsync()
}
CloudSdkVersions version = await GetJsonOutputAsync("version");
- return new Version(version.SdkVersion);
+ return version.SdkVersion;
}
private async Task GetJsonOutputAsync(string command)
diff --git a/GoogleCloudExtension/GoogleCloudExtension/GoogleCloudExtension.csproj b/GoogleCloudExtension/GoogleCloudExtension/GoogleCloudExtension.csproj
index 96a67e319..049937f3d 100644
--- a/GoogleCloudExtension/GoogleCloudExtension/GoogleCloudExtension.csproj
+++ b/GoogleCloudExtension/GoogleCloudExtension/GoogleCloudExtension.csproj
@@ -44,6 +44,7 @@
4truefalse
+ 7.1pdbonly
@@ -54,6 +55,7 @@
4truefalse
+ 7.1
diff --git a/GoogleCloudExtension/GoogleCloudExtensionUnitTests/GCloud/GCloudContextUnitTests.cs b/GoogleCloudExtension/GoogleCloudExtensionUnitTests/GCloud/GCloudContextUnitTests.cs
index 3b1fef3e1..3776a76fd 100644
--- a/GoogleCloudExtension/GoogleCloudExtensionUnitTests/GCloud/GCloudContextUnitTests.cs
+++ b/GoogleCloudExtension/GoogleCloudExtensionUnitTests/GCloud/GCloudContextUnitTests.cs
@@ -13,11 +13,14 @@
// limitations under the License.
using GoogleCloudExtension.GCloud;
+using GoogleCloudExtension.GCloud.Models;
using GoogleCloudExtension.Utils;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using System;
using System.Collections.Generic;
+using System.Linq;
+using System.Linq.Expressions;
using System.Threading.Tasks;
namespace GoogleCloudExtensionUnitTests.GCloud
@@ -36,11 +39,34 @@ public class GCloudContextUnitTests : ExtensionTestBase
private GCloudContext _objectUnderTest;
private Mock _processServiceMock;
private Action _mockedOutputAction;
+ private TaskCompletionSource _versionResultSource;
+
+ ///
+ /// A version of Google Cloud SDK that includes the gcloud builds commands.
+ ///
+ private static readonly CloudSdkVersions s_buildsEnabledSdkVersion =
+ new CloudSdkVersions { SdkVersion = new Version(GCloudContext.GCloudBuildsMinimumVersion) };
+
+ ///
+ /// A version of Google Cloud SDK from before the gcloud builds commands were added.
+ ///
+ private static readonly CloudSdkVersions s_buildsMissingSdkVersion =
+ new CloudSdkVersions { SdkVersion = new Version(GCloudWrapper.GCloudSdkMinimumVersion) };
+
+ ///
+ /// Used as dynamic data to test that container builder arguments do not change between versions.
+ ///
+ private static IEnumerable