- Create a new solution and copy-paste this code.
- Repalce
### YOUR_KEY ###
and### YOUR_IV ###
with your values. This is done so that ACR credentials aren't stored open. The whole AES part is functionally unnecessary and isn't in fact secure, jsut better than nothing. - Find where the original helm 3 executable is located on your OcropusDeploy server.
- Rename it from
helm.exe
to_helm.exe
- Copy the executable built from this repository with
dotnet publish -r win-x64 -c Release /p:PublishSingleFile=true /p:PublishTrimmed=true
into the same folder and name ithelm.exe
. - PROFIT
Last active
January 21, 2020 22:36
-
-
Save ilyalukyanov/c2c37d7a0fae85c177e8da6999de89e3 to your computer and use it in GitHub Desktop.
Shim to add support for Helm 3 and ACR into Octopus Deploy 2019.9.12
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Diagnostics; | |
using System.IO; | |
using System.Linq; | |
using System.Security.Cryptography; | |
using System.Text; | |
using System.Text.RegularExpressions; | |
using System.Threading.Tasks; | |
namespace HelmProxy | |
{ | |
internal static class Program | |
{ | |
private const string ThisApplicationHomeDir = @"C:\helm-proxy"; | |
static async Task Main(string[] args) | |
{ | |
if (!Directory.Exists(ThisApplicationHomeDir)) Directory.CreateDirectory(ThisApplicationHomeDir); | |
var argsString = string.Join(" ", args); | |
using var aes = Aes.Create(); | |
aes.Key = Convert.FromBase64String("### YOUR_KEY ###"); | |
aes.IV = Convert.FromBase64String("### YOUR_IV ###"); | |
var exitCode = -1; | |
var command = args.FirstOrDefault(); | |
if (command != null) | |
{ | |
switch (command) | |
{ | |
case "init": | |
// Unsupported, just silently ignoring this command | |
return; | |
case "repo": | |
{ | |
/* | |
* ACR doesn't allow to do repo add with ACR credentials. Instead it requires calling `az acr helm repo add` that creates does pretty much the same except with a temporary token. | |
* I wasn't sure if Octopus will call repo add each time a package is downloaded, so decided to mimic its behaviour. | |
* Feel free to experiment. | |
* | |
* Parsing command: | |
* repo add --home C:\Windows\TEMP\helm\68394d8c9e3bd14448357c8b\helm --username <user_name> --password <pwd> <feed_name> <repo_url> | |
*/ | |
var username = args[5]; | |
var password = args[7]; | |
var feedName = args[8]; | |
var host = new UriBuilder(args[9]) {Path = ""}.ToString(); | |
var (fileName, rawSize, encSize) = | |
await HelmRepoAddAsync(aes, feedName, host, username, password); | |
return; | |
} | |
case "fetch": | |
{ | |
/* | |
* Converting this: fetch --home <home> --version <version> --destination <destination> <feed_name>/<package_name> | |
* Into this: fetch https://<feed_name>/.azurecr.io/helm/v1/repo/_blobs/<package_name>.tgz --version <version> --destination <destination> | |
*/ | |
var packageAddrMatches = Regex.Matches(args[7], @"^([^/]+)/([^/]+)$")[0].Groups; | |
var (host, username, password) = | |
await HelmRepoGetAsync(aes, Convert.ToString(packageAddrMatches[1])); | |
var packageUrl = $"{host}/helm/v1/repo/_blobs/{packageAddrMatches[2]}-{args[4]}.tgz"; | |
argsString = string.Join(" ", args[0], packageUrl, args[5], args[6], $"--username {username}", | |
$"--password {password}"); | |
break; | |
} | |
} | |
} | |
// This presumes you have renamed helm.exe to _helm.exe in its original location | |
using var process = Process.Start( | |
new ProcessStartInfo("_helm", argsString) | |
{ | |
RedirectStandardError = true, | |
RedirectStandardOutput = true | |
}); | |
process.WaitForExit(); | |
var outputText = await process.StandardOutput.ReadToEndAsync(); | |
if (outputText.Length > 0) await Console.Out.WriteAsync(outputText); | |
var errorText = await process.StandardError.ReadToEndAsync(); | |
if (errorText.Length > 0) await Console.Error.WriteAsync(errorText); | |
exitCode = process.ExitCode; | |
Environment.Exit(exitCode); | |
} | |
private static async Task<(string fileName, int rawPayloadLength, int encryptedPayloadLength)> HelmRepoAddAsync( | |
Aes aes, string feedName, string host, string username, string password) | |
{ | |
var tempRepoCredentialsFile = Path.Combine(ThisApplicationHomeDir, | |
$"helm-repo-{feedName}.txt"); | |
using var encryptor = aes.CreateEncryptor(); | |
await using var helmRepoFile = File.Create(tempRepoCredentialsFile); | |
await using var aesStream = | |
new CryptoStream(helmRepoFile, encryptor, CryptoStreamMode.Write); | |
var repoPayload = Encoding.UTF8.GetBytes(string.Join(" ", host, username, password)); | |
await aesStream.WriteAsync(repoPayload, 0, repoPayload.Length); | |
aesStream.FlushFinalBlock(); | |
await aesStream.FlushAsync(); | |
await helmRepoFile.FlushAsync(); | |
return (tempRepoCredentialsFile, repoPayload.Length, (int) helmRepoFile.Length); | |
} | |
private static async Task<(string host, string username, string password)> HelmRepoGetAsync(Aes aes, | |
string feedName) | |
{ | |
var tempRepoCredentialsFile = Path.Combine(ThisApplicationHomeDir, | |
$"helm-repo-{feedName}.txt"); | |
using var decryptor = aes.CreateDecryptor(); | |
await using var helmRepoFile = File.OpenRead(tempRepoCredentialsFile); | |
await using var aesStream = | |
new CryptoStream(helmRepoFile, decryptor, CryptoStreamMode.Read); | |
await using var memoryStream = new MemoryStream(); | |
await aesStream.CopyToAsync(memoryStream); | |
await memoryStream.FlushAsync(); | |
var repoDetails = Encoding.UTF8.GetString(memoryStream.ToArray()).Split(" "); | |
return (repoDetails[0], repoDetails[1], repoDetails[2]); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment