From c5248e4fc1d707a58aec1d0f3d2ea5567f6b129d Mon Sep 17 00:00:00 2001 From: Jacob Affinito Date: Wed, 2 Aug 2023 08:41:18 -0700 Subject: [PATCH 1/2] chore: Add S3 Validation tool. (#1811) --- build/BuildTools.sln | 6 ++ build/S3Validator/Configuration.cs | 17 ++++ build/S3Validator/ExitCode.cs | 13 +++ build/S3Validator/FileDetails.cs | 14 +++ build/S3Validator/Options.cs | 16 ++++ build/S3Validator/Program.cs | 126 +++++++++++++++++++++++++++ build/S3Validator/S3Validator.csproj | 26 ++++++ build/S3Validator/config.yml | 66 ++++++++++++++ 8 files changed, 284 insertions(+) create mode 100644 build/S3Validator/Configuration.cs create mode 100644 build/S3Validator/ExitCode.cs create mode 100644 build/S3Validator/FileDetails.cs create mode 100644 build/S3Validator/Options.cs create mode 100644 build/S3Validator/Program.cs create mode 100644 build/S3Validator/S3Validator.csproj create mode 100644 build/S3Validator/config.yml diff --git a/build/BuildTools.sln b/build/BuildTools.sln index 2789b312c7..9637bf69f2 100644 --- a/build/BuildTools.sln +++ b/build/BuildTools.sln @@ -7,6 +7,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ArtifactBuilder", "Artifact EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NewRelic.NuGetHelper", "NewRelic.NuGetHelper\NewRelic.NuGetHelper.csproj", "{94BF8D27-2122-4573-AA79-90B977B40EF3}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "S3Validator", "S3Validator\S3Validator.csproj", "{648D08B2-E677-4009-A593-D03E0579E859}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -21,6 +23,10 @@ Global {94BF8D27-2122-4573-AA79-90B977B40EF3}.Debug|Any CPU.Build.0 = Debug|Any CPU {94BF8D27-2122-4573-AA79-90B977B40EF3}.Release|Any CPU.ActiveCfg = Release|Any CPU {94BF8D27-2122-4573-AA79-90B977B40EF3}.Release|Any CPU.Build.0 = Release|Any CPU + {648D08B2-E677-4009-A593-D03E0579E859}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {648D08B2-E677-4009-A593-D03E0579E859}.Debug|Any CPU.Build.0 = Debug|Any CPU + {648D08B2-E677-4009-A593-D03E0579E859}.Release|Any CPU.ActiveCfg = Release|Any CPU + {648D08B2-E677-4009-A593-D03E0579E859}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/build/S3Validator/Configuration.cs b/build/S3Validator/Configuration.cs new file mode 100644 index 0000000000..8950f1ae2b --- /dev/null +++ b/build/S3Validator/Configuration.cs @@ -0,0 +1,17 @@ +// Copyright 2020 New Relic, Inc. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +namespace S3Validator +{ + public class Configuration + { + [YamlDotNet.Serialization.YamlMember(Alias = "base-url")] + public string? BaseUrl { get; set; } + + [YamlDotNet.Serialization.YamlMember(Alias = "directory-list")] + public List? DirectoryList { get; set; } + + [YamlDotNet.Serialization.YamlMember(Alias = "file-list")] + public List? FileList { get; set; } + } +} diff --git a/build/S3Validator/ExitCode.cs b/build/S3Validator/ExitCode.cs new file mode 100644 index 0000000000..b1bff6ba40 --- /dev/null +++ b/build/S3Validator/ExitCode.cs @@ -0,0 +1,13 @@ +// Copyright 2020 New Relic, Inc. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +namespace S3Validator +{ + enum ExitCode : int + { + Success = 0, + Error = 1, + FileNotFound = 2, + BadArguments = 160 + } +} diff --git a/build/S3Validator/FileDetails.cs b/build/S3Validator/FileDetails.cs new file mode 100644 index 0000000000..1f6067122c --- /dev/null +++ b/build/S3Validator/FileDetails.cs @@ -0,0 +1,14 @@ +// Copyright 2020 New Relic, Inc. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +namespace S3Validator +{ + public struct FileDetails + { + [YamlDotNet.Serialization.YamlMember(Alias = "name")] + public string Name { get; set; } + + [YamlDotNet.Serialization.YamlMember(Alias = "size")] + public long Size { get; set; } + } +} diff --git a/build/S3Validator/Options.cs b/build/S3Validator/Options.cs new file mode 100644 index 0000000000..b4742914b8 --- /dev/null +++ b/build/S3Validator/Options.cs @@ -0,0 +1,16 @@ +// Copyright 2020 New Relic, Inc. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +using CommandLine; + +namespace S3Validator +{ + public class Options + { + [Option('v', "version", Required = true, HelpText = "Version to validate.")] + public required string Version { get; set; } + + [Option('c', "config", Default = "config.yml", Required = false, HelpText = "Path to the configuration file.")] + public required string ConfigurationPath { get; set; } + } +} diff --git a/build/S3Validator/Program.cs b/build/S3Validator/Program.cs new file mode 100644 index 0000000000..6a6a350c4b --- /dev/null +++ b/build/S3Validator/Program.cs @@ -0,0 +1,126 @@ +// Copyright 2020 New Relic, Inc. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +using CommandLine; + +namespace S3Validator +{ + internal class Program + { + private const string VersionToken = @"{version}"; + + private static readonly HttpClient _client = new(); + + static void Main(string[] args) + { + var result = Parser.Default.ParseArguments(args) + .WithParsed(ValidateOptions) + .WithNotParsed(HandleParseError); + + var version = result.Value.Version; + var configuration = LoadConfiguration(result.Value.ConfigurationPath); + Validate(version, configuration); + Console.WriteLine("Valid."); + } + + private static void Validate(string version, Configuration configuration) + { + var tasks = new List>(); + foreach (var dir in configuration.DirectoryList!) + { + foreach (var fileDetail in configuration.FileList!) + { + var url = $"{configuration.BaseUrl}/{dir.Replace(VersionToken, version)}/{fileDetail.Name.Replace(VersionToken, version)}"; + var request = new HttpRequestMessage(HttpMethod.Head, url); + request.Options.TryAdd("expectedSize", fileDetail.Size); + tasks.Add(_client.SendAsync(request)); + } + } + + if (!tasks.Any()) + { + ExitWithError(ExitCode.Error, "There was nothing to validate."); + } + + var taskCompleted = Task.WaitAll(tasks.ToArray(), 10000); + if (!taskCompleted) + { + ExitWithError(ExitCode.Error, "Validation timed out waiting for HttpClient requests to complete."); + } + + var isValid = true; + var results = new List(); + foreach (var task in tasks) + { + var status = "Valid"; + + if (!task.Result.IsSuccessStatusCode) + { + isValid = false; + status = task.Result.StatusCode.ToString(); + } + else if (task.Result.Content.Headers.ContentLength < task.Result.RequestMessage?.Options.GetValue("expectedSize")) + { + isValid = false; + status = "FileSize"; + } + + results.Add($"{status,-12}{task.Result.RequestMessage?.RequestUri}"); + } + + if (!isValid) + { + ExitWithError(ExitCode.Error, "Validation failed. Results:" + Environment.NewLine + string.Join(Environment.NewLine, results)); + } + } + + private static void ValidateOptions(Options opts) + { + if (string.IsNullOrWhiteSpace(opts.Version) + || string.IsNullOrWhiteSpace(opts.ConfigurationPath)) + { + ExitWithError(ExitCode.BadArguments, "Arguments were empty or whitespace."); + } + + if (!Version.TryParse(opts.Version, out _)) + { + ExitWithError(ExitCode.Error, $"Version provided, '{opts.Version}', was not a valid version."); + } + + if (!File.Exists(opts.ConfigurationPath)) + { + ExitWithError(ExitCode.FileNotFound, $"Configuration file did not exist at {opts.ConfigurationPath}."); + } + } + + private static Configuration LoadConfiguration(string path) + { + var input = File.ReadAllText(path); + var deserializer = new YamlDotNet.Serialization.Deserializer(); + return deserializer.Deserialize(input); + } + + private static void HandleParseError(IEnumerable errs) + { + ExitWithError(ExitCode.BadArguments, "Error occurred while parsing command line arguments."); + } + + public static void ExitWithError(ExitCode exitCode, string message) + { + Console.WriteLine(message); + Environment.Exit((int)exitCode); + } + } + + public static class Helpers + { + + // Simplfy the TryGetValue into something more usable. + public static T? GetValue(this HttpRequestOptions options, string key) + { + options.TryGetValue(new HttpRequestOptionsKey(key), out var value); + return value; + } + } + +} diff --git a/build/S3Validator/S3Validator.csproj b/build/S3Validator/S3Validator.csproj new file mode 100644 index 0000000000..0219c691e2 --- /dev/null +++ b/build/S3Validator/S3Validator.csproj @@ -0,0 +1,26 @@ + + + + Exe + net7.0 + 11.0 + enable + enable + + + + + + + + + PreserveNewest + + + + + + + + + diff --git a/build/S3Validator/config.yml b/build/S3Validator/config.yml new file mode 100644 index 0000000000..d0f230f03b --- /dev/null +++ b/build/S3Validator/config.yml @@ -0,0 +1,66 @@ +# Copyright 2020 New Relic, Inc. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +# BaseUrl +# Should include the full path the to root of the agent folder - no trailing slash. Example: https://download.newrelic.com/dot_net_agent +base-url: https://download.newrelic.com/dot_net_agent + +# DirectoryList +# A list of sub directories to check - no leading or trailing slash. The FileList will be checked in each directory. +# Use '{version}' in places where the version would be found. This will be replaced with the version supplied to the tool on execution. +# Example: 'previous_releases/10.13.0' becomes 'previous_releases/{version}' +directory-list: + - latest_release + - previous_releases/{version} + +# FileList +# Use the relative path, starting at each directory in DirectoryList, to each file and a minimum acceptable size. +# Do not use the exact size - this file should not need to be update just to change sizes under normal circumstances. +# Use '{version}' in places where the version would be found. This will be replaced with the version supplied to the tool on execution. +# Example: 'NewRelicDotNetAgent_10.13.0_x64.msi' becomes 'NewRelicDotNetAgent_{version}_x64.msi' +file-list: + - name: NewRelicDotNetAgent_{version}_x64.msi + size: 13000000 + - name: NewRelicDotNetAgent_{version}_x64.zip + size: 11500000 + - name: NewRelicDotNetAgent_{version}_x86.msi + size: 12500000 + - name: NewRelicDotNetAgent_{version}_x86.zip + size: 11500000 + - name: NewRelicDotNetAgent_x64.msi + size: 13000000 + - name: NewRelicDotNetAgent_x86.msi + size: 12500000 + - name: Readme.txt + size: 1500 + - name: newrelic-dotnet-agent-{version}-1.x86_64.rpm + size: 3000000 + - name: newrelic-dotnet-agent_{version}_amd64.deb + size: 2500000 + - name: newrelic-dotnet-agent_{version}_amd64.tar.gz + size: 3900000 + - name: newrelic-dotnet-agent_{version}_arm64.deb + size: 2100000 + - name: newrelic-dotnet-agent_{version}_arm64.tar.gz + size: 3700000 + - name: SHA256/NewRelicDotNetAgent_{version}_x64.msi.sha256 + size: 58 + - name: SHA256/NewRelicDotNetAgent_{version}_x64.zip.sha256 + size: 58 + - name: SHA256/NewRelicDotNetAgent_{version}_x86.msi.sha256 + size: 58 + - name: SHA256/NewRelicDotNetAgent_{version}_x86.zip.sha256 + size: 58 + - name: SHA256/checksums.md + size: 800 + - name: SHA256/newrelic-dotnet-agent-{version}-1.x86_64.rpm.sha256 + size: 95 + - name: SHA256/newrelic-dotnet-agent_{version}_amd64.deb.sha256 + size: 95 + - name: SHA256/newrelic-dotnet-agent_{version}_amd64.tar.gz.sha256 + size: 95 + - name: SHA256/newrelic-dotnet-agent_{version}_arm64.deb.sha256 + size: 95 + - name: SHA256/newrelic-dotnet-agent_{version}_arm64.tar.gz.sha256 + size: 95 + From bbdad76352cf848b0732101747f2d4a1682b9d05 Mon Sep 17 00:00:00 2001 From: Marty Tippin <120425148+tippmar-nr@users.noreply.github.com> Date: Wed, 2 Aug 2023 11:20:47 -0500 Subject: [PATCH 2/2] Update APM version on deploy --- .github/workflows/deploy_agent.yml | 38 ++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/.github/workflows/deploy_agent.yml b/.github/workflows/deploy_agent.yml index 74b94ccd00..7b4bd40592 100644 --- a/.github/workflows/deploy_agent.yml +++ b/.github/workflows/deploy_agent.yml @@ -33,6 +33,10 @@ on: description: 'If "true", will run the index-download-site job. If "false", will not.' required: true default: 'true' + update-apm-version: + description: 'If "true", will run the update-apm job. If "false", will not.' + required: true + default: 'true' permissions: contents: read @@ -334,3 +338,37 @@ jobs: run: | curl -i -X POST -H 'Fastly-Key:${{ secrets.FASTLY_TOKEN }}' ${{ secrets.FASTLY_URL }} shell: bash + + update-apm: + name: Update System Configuration Page + runs-on: ubuntu-latest + if: ${{ github.event.inputs.update-apm-version == true }} + steps: + - name: Update system configuration page + run: | + PAYLOAD="{ + \"system_configuration\": { + \"key\": \"dotnet_agent_version\", + \"value\": \"${{ github.event.inputs.agent_version }}\" + } + }" + CONTENT_TYPE='Content-Type: application/json' + + # STAGING + curl -X POST 'https://staging-api.newrelic.com/v2/system_configuration.json' \ + -H "X-Api-Key:${{ secrets.NEW_RELIC_API_KEY_STAGING }}" -i \ + -H "$CONTENT_TYPE" \ + -d "$PAYLOAD" + + # PRODUCTION + curl -X POST 'https://api.newrelic.com/v2/system_configuration.json' \ + -H "X-Api-Key:${{ secrets.NEW_RELIC_API_KEY_PRODUCTION }}" -i \ + -H "$CONTENT_TYPE" \ + -d "$PAYLOAD" + + # EU PRODUCTION + curl -X POST 'https://api.eu.newrelic.com/v2/system_configuration.json' \ + -H "X-Api-Key:${{ secrets.NEW_RELIC_API_KEY_PRODUCTION }}" -i \ + -H "$CONTENT_TYPE" \ + -d "$PAYLOAD" +