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);
            }
        }
Example #2
0
        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);
        }
Example #4
0
        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()));
        }
Example #5
0
        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);
                });
            });
        }
Example #6
0
        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);
        }