public async Task UploadConfiguration(string contents, string destinationPath) { if (_s3Client == null) { _s3Client = new AmazonS3Client(Amazon.RegionEndpoint.USEast1); } var cts = new CancellationTokenSource(); cts.CancelAfter(s3UploadTimeoutMs); PutObjectRequest request = new PutObjectRequest() { BucketName = UploadBucket, Key = destinationPath, ContentBody = contents }; try { Output.Verbose($"Writing object to {request.Key}"); PutObjectResponse result = await _s3Client.PutObjectAsync(request, cts.Token); if (result.HttpStatusCode != System.Net.HttpStatusCode.OK) { throw new System.Exception($"Unable to upload config file to s3 {result.ToString()}"); } Output.Success($"Service Configuraiton written to {request.Key}"); } catch (System.Exception ex) { throw new System.Exception("Unable to upload config file to s3", ex); } }
internal static async Task <ProcessResults> RunCommandAsync( string filename, string arguments, string workingDirectory = null, bool throwOnErrorExitCode = true, string errorMessage = null, CancellationToken?token = null) { // Output.Info($"Executing async command >{filename} {arguments}"); var results = await ProcessEx.RunAsync(filename, arguments, workingDirectory); if (results.ExitCode != 0 && throwOnErrorExitCode) { var commandLog = $"Command failed with exit code {results.ExitCode}: > {filename} {arguments}"; if (token?.IsCancellationRequested != true) { if (!string.IsNullOrWhiteSpace(errorMessage)) { Output.Error(errorMessage); } Output.Error(commandLog); var indent = "==>"; Output.Error($"StandardError:\n{indent}{string.Join($"\n{indent}", results.StandardError)}"); Output.Verbose($"StandardOutput:\n{indent}{string.Join($"\n{indent}", results.StandardOutput)}"); } throw new Exception(commandLog); } return(results); }
public static async Task <string> GetFriendlyHostName() { if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) { // Using LocalHostName config on a Mac because HostName commonly isn't set, resulting in something like "ip-192-168-1-127" // from Environment.MachineName (as well as the `hostname` command). LocalHostName is also easily configurable via the Sharing panel in System Preferences. var localHostName = (await CommandUtilities.RunCommandAsync("scutil", "--get LocalHostName")).StandardOutput.FirstOrDefault()?.ToLower(); // TODO - ensure localHostName is valid subdomain if (!string.IsNullOrWhiteSpace(localHostName)) { Output.Verbose($"[{nameof(NetworkUtility.GetFriendlyHostName)}] Using macOS local hostname (configurable via \"System Preferences > Sharing\"): {localHostName}"); return(localHostName); } } var machineName = Environment.MachineName; Output.Verbose($"[{nameof(NetworkUtility.GetFriendlyHostName)}] Using general machine name: {machineName}"); return(machineName); }
protected override async Task <int> Run(MarvelMicroserviceConfig config) { foreach (var kvp in new Dictionary <string, CommandOption> { { "branch", _branchOption }, { "git SHA", _gitShaOption }, { "build number", _buildNumberOption }, { "NuGet API key", _nugetApiKeyOption }, { "AWS access key id", _awsAccessKeyOption }, { "AWS secret key", _awsAccessSecretOption }, { "persist static assets", _persistStaticAssetsOption }, }) { Output.Verbose($"Option {kvp.Key}: {kvp.Value.Value()}"); } var paramValidationErrors = ValidateParams(); if (paramValidationErrors.Any()) { paramValidationErrors.ForEach(Output.Error); return(1); } var persistStaticAssets = _persistStaticAssetsOption.HasValue(); if (!persistStaticAssets) { Output.Info($"NOTE: Static assets will not be published to a persistent CDN. They will be temporarily available (minimum of 1 day) for testing with this build. " + $"If this build is for a deployable app (e.g., a CI build for either prod or thor environments, or a locally-built emergency deploy), " + $"you should specify the {_persistStaticAssetsOption.LongName} option. If this is a local build for manual testing or an automated testing run, you may ignore this."); } _assetHostConfigurationAccessor.SetValue(persistStaticAssets ? S3AssetHostConfiguration.ProductionAssets : S3AssetHostConfiguration.TestAssets); return(await Publisher.Publish(config, _buildConfigurationBuilder, _configurationFileMerger, _configurationFileUploader, _configurationFileValidator, _staticAssetProcessor, _nugetApiKeyOption.Value(), _awsAccessKeyOption.Value(), _awsAccessSecretOption.Value(), _branchOption.Value(), _gitShaOption.Value(), _buildNumberOption.Value(), _mergeAndUploadServiceConfig.HasValue(), _mergeServiceConfig.HasValue())); }
public void Register(CommandLineApplication app, string currentDirectory, CommandOption verboseOption) { app.Command(CommandName, command => { command.Description = Description; AddHelpOption(command); CreateOptions(command); var commandVerboseOption = AddVerboseOption(command); command.OnExecute(() => { // Allow `-v` to be added before or after the subcommand, i.e., `dotnet marvel -v build` or `dotnet marvel build -v` Output.UseVerbose = verboseOption.HasValue() || commandVerboseOption.HasValue(); var config = new MarvelMicroserviceConfig(currentDirectory); Output.Info($"Running `dotnet {Program.CliCommandName} {CommandName}`..."); Output.Verbose("Using verbose logging"); Output.Info($"Using solution at {config.BaseDirectory}"); TrySetTestAuthentication(); var task = Task.Run(async() => await Run(config)); var code = task.GetAwaiter().GetResult(); if (code == 0) { Output.Success("Success."); } else { Output.Error("Failed."); } return(code); }); }); }
private static async Task <GetAuthorizationTokenResponse> GetAuthorizationToken(string accessKeyId, string secretKey) { Output.Verbose($"Retrieving authorization token {nameof(accessKeyId)}={accessKeyId}, {nameof(secretKey)}={secretKey}."); GetAuthorizationTokenResponse authorizationToken; // We only want to get & save secrets from/to local configuration when we have not provided them var needToStoreSecrets = false; if (accessKeyId == null || secretKey == null) { Output.Verbose("Attempting to load AWS credentials from secrets store."); var securityConfig = GetConfiguration(); if (accessKeyId == null) { accessKeyId = securityConfig[SecretStoreKeys.AccessKeyId]; } if (secretKey == null) { secretKey = securityConfig[SecretStoreKeys.SecretKey]; } } var firstPrompt = true; do { if (accessKeyId == null || secretKey == null) { Output.Verbose("Attempting to prompt for AWS credentials."); needToStoreSecrets = true; if (!ConsoleHelper.IsInteractive) { // CLI is running from within a script in a non-interactive terminal, e.g., from pressing F5 in VS Code. The // auth command will get users to this same code path, but in an interactive terminal. var cliCommand = Program.CliCommandName; var authCommand = new AuthorizeCommand().CommandName; Output.Error(Output.HorizontalLine()); Output.Error("Your machine is missing necessary AWS credentials. Authorize your machine by opening a terminal and " + "running the following commands, then try your actions again. This authorization only needs to be performed once per user/machine:"); Output.Error($"> cd {Directory.GetCurrentDirectory()}"); Output.Error($"> dotnet {cliCommand} {authCommand}"); Output.Error($"If you have further issues, please contact #watch-ops."); Output.Error(Output.HorizontalLine()); return(null); } // TODO add link to docs page for finding/creating AWS access keys and getting help from watch-ops to set up account // or add permissions. if (firstPrompt) { Output.Error("Your machine is missing necessary AWS credentials. The following authorization only needs to be performed " + "once per user/machine. For assistance, please contact #watch-ops."); firstPrompt = false; } Output.Error("Paste in your AWS ACCESS KEY ID:"); accessKeyId = ConsoleHelper.ReadPassword(); Output.Error("Now paste in your AWS SECRET ACCESS KEY:"); secretKey = ConsoleHelper.ReadPassword(); } try { var ecrClient = new AmazonECRClient(accessKeyId, secretKey, _regionEndpoint); // TODO Potentially slow. We might need to create a cached toked since it'll be valid for 12 hours // Another thing is that there is a throttling for GetAuthorizationTokenAsync (1 call / second) // There's not a good way to check the stored credentials without making // an actual call to ECR (e.g. a docker pull). Doing that on every operation // would be relatively slow. Could we write a hidden file on successful auth // check and expire it after 8 hours? Output.Verbose($"Retrieving ECR authentication token"); authorizationToken = await ecrClient.GetAuthorizationTokenAsync(new GetAuthorizationTokenRequest(), new CancellationTokenSource(_awsTimeout).Token); // We have successfully got authorization token let's save our access key and secret in user-secrets. // We only save them when we've not provided access and secret via parameters. if (needToStoreSecrets) { Output.Verbose($"Storing secret {SecretStoreKeys.AccessKeyId}"); await SaveSecret(SecretStoreKeys.AccessKeyId, accessKeyId); Output.Verbose($"Storing secret {SecretStoreKeys.SecretKey}"); await SaveSecret(SecretStoreKeys.SecretKey, secretKey); } break; } catch (Exception e) { var amazonEcrException = e.InnerException as AmazonECRException; if (amazonEcrException != null && amazonEcrException.ErrorCode.Equals("UnrecognizedClientException")) { Output.VerboseException(e); Output.Error("Bad credentials. Please try again."); accessKeyId = null; secretKey = null; continue; } // TODO handle case where permissions are inadequate (?) Output.Error("Something went wrong while connecting to AWS. Please try again later."); Output.Exception(e); throw; } } while (true); return(authorizationToken); }