migrated to go, add GAR support

This commit is contained in:
Akshit Agrawal
2024-01-12 11:56:42 +05:30
parent 8638190887
commit 5f413c0071
8 changed files with 158 additions and 215 deletions
+6 -10
View File
@@ -1,18 +1,14 @@
FROM --platform=linux/amd64 alpine:3.18 as base
FROM --platform=linux/amd64 alpine:latest as base
WORKDIR /app
ENV HELM_EXPERIMENTAL_OCI=1
RUN apk --no-cache add ca-certificates curl && \
curl -LO "https://get.helm.sh/helm-v3.7.0-linux-amd64.tar.gz" && \
tar -zxvf helm-v3.7.0-linux-amd64.tar.gz && \
curl -LO "https://get.helm.sh/helm-v3.8.0-linux-amd64.tar.gz" && \
tar -zxvf helm-v3.8.0-linux-amd64.tar.gz && \
mv linux-amd64/helm /usr/local/bin/helm && \
rm -rf linux-amd64 helm-v3.7.0-linux-amd64.tar.gz && \
rm -rf linux-amd64 helm-v3.8.0-linux-amd64.tar.gz && \
chmod +x /usr/local/bin/helm
RUN apk add --no-cache python3
COPY ./drone-helm /app/
COPY ./main.py /app/
CMD ["python", "/app/main.py"]
CMD ["/app/drone-helm"]
+7 -11
View File
@@ -1,18 +1,14 @@
FROM --platform=linux/arm64 alpine:3.18 as base
FROM --platform=linux/arm64 alpine:latest as base
WORKDIR /app
ENV HELM_EXPERIMENTAL_OCI=1
RUN apk --no-cache add ca-certificates curl && \
curl -LO "https://get.helm.sh/helm-v3.7.0-linux-arm64.tar.gz" && \
tar -zxvf helm-v3.7.0-linux-arm64.tar.gz && \
mv linux-arm64/helm /usr/local/bin/helm && \
rm -rf linux-arm64 helm-v3.7.0-linux-arm64.tar.gz && \
curl -LO "https://get.helm.sh/helm-v3.8.0-linux-amd64.tar.gz" && \
tar -zxvf helm-v3.8.0-linux-amd64.tar.gz && \
mv linux-amd64/helm /usr/local/bin/helm && \
rm -rf linux-amd64 helm-v3.8.0-linux-amd64.tar.gz && \
chmod +x /usr/local/bin/helm
RUN apk add --no-cache python3
COPY ./drone-helm /app/
COPY ./main.py /app/
CMD ["python", "/app/main.py"]
CMD ["/app/drone-helm"]
+2 -4
View File
@@ -13,8 +13,6 @@ RUN Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointMa
RUN choco install -y kubernetes-helm
RUN choco install -y python3
COPY ./drone-helm.exe /app/
COPY ./main.py /app/
ENTRYPOINT ["python", "C:\\app\\main.py"]
CMD ["C:\\app\\drone-helm.exe"]
-20
View File
@@ -1,20 +0,0 @@
FROM --platform=windows/amd64 mcr.microsoft.com/windows/servercore:ltsc2022
USER ContainerAdministrator
WORKDIR C:\\app
ENV HELM_EXPERIMENTAL_OCI=1
ENV chocolateyVersion=1.4.0
SHELL ["powershell", "-Command", "$ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue';"]
RUN Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))
RUN choco install -y kubernetes-helm
RUN choco install -y python3
COPY ./main.py /app/
ENTRYPOINT ["python", "C:\\app\\main.py"]
+3
View File
@@ -0,0 +1,3 @@
module drone/plugin/helm-chart-docker-registry
go 1.21.5
+140
View File
@@ -0,0 +1,140 @@
package main
import (
"bytes"
"fmt"
"os"
"os/exec"
"runtime"
"strings"
)
func main() {
// get environment variables
registry := os.Getenv("PLUGIN_REGISTRY")
username := os.Getenv("PLUGIN_USERNAME")
token := os.Getenv("PLUGIN_TOKEN")
chartPath := os.Getenv("PLUGIN_CHART_PATH")
namespace := os.Getenv("PLUGIN_NAMESPACE")
if (registry == "") || (username == "") || (token == "") || (namespace == "") {
fmt.Println("Missing required environment variables")
os.Exit(1)
}
if chartPath == "" {
chartPath = "./"
}
// get chart name
chartName, err := getChartName(chartPath)
if err != nil {
fmt.Println("Failed to get chart name")
os.Exit(1)
}
chartName = strings.TrimSpace(chartName)
// get chart version
chartVersion, err := getChartVersion(chartPath)
if err != nil {
fmt.Println("Failed to get chart version")
os.Exit(1)
}
chartVersion = strings.TrimSpace(chartVersion)
// cd into chart path
if err := os.Chdir(chartPath); err != nil {
fmt.Println(err)
os.Exit(1)
}
// package chart
packageChartCmd := exec.Command("helm", "package", "--dependency-update", ".")
if err := packageChartCmd.Run(); err != nil {
fmt.Println(err)
os.Exit(1)
}
// get chart tarball name
chartTarballName := fmt.Sprintf("%s-%s.tgz", chartName, chartVersion)
fmt.Println("Chart packaged successfully - ", chartTarballName)
// login to registry
loginCmd := exec.Command("helm", "registry", "login", registry, "--username", username, "--password", token)
if err := loginCmd.Run(); err != nil {
fmt.Println("Failed to login to registry")
os.Exit(1)
}
fmt.Println("Logged in to registry successfully")
// push chart
pushChartCmd := exec.Command("helm", "push", chartTarballName, fmt.Sprintf("oci://%s/%s", registry, namespace))
if err := pushChartCmd.Run(); err != nil {
fmt.Println("Failed to push chart")
os.Exit(1)
}
fmt.Println("Chart pushed successfully - ", registry, "/", namespace, "/", chartName)
}
func getChartName(chartPath string) (string, error) {
// helm show chart chartPath | grep name | awk '{print $2}'
cmd1 := exec.Command("helm", "show", "chart", chartPath)
cmd2 := exec.Command("awk", "/name:/ {print $2}")
// if os is windows
if runtime.GOOS == "windows" {
cmd1 = exec.Command("helm", "show", "chart", chartPath)
cmd2 = exec.Command("powershell", "-Command", "Select-String 'name:' | ForEach-Object { $_.Matches.Groups[1].Value }")
}
var out1 bytes.Buffer
var out2 bytes.Buffer
cmd1.Stdout = &out1
cmd2.Stdin = &out1
cmd2.Stdout = &out2
if err := cmd1.Run(); err != nil {
fmt.Println("Chart does not exist in the specified path")
return "", err
}
if err := cmd2.Run(); err != nil {
return "", err
}
return out2.String(), nil
}
func getChartVersion(chartPath string) (string, error) {
// helm show chart chartPath | grep version | awk '{print $2}'
cmd1 := exec.Command("helm", "show", "chart", chartPath)
cmd2 := exec.Command("awk", "/version:/ {print $2}")
// if os is windows
if runtime.GOOS == "windows" {
cmd1 = exec.Command("helm", "show", "chart", chartPath)
cmd2 = exec.Command("powershell", "-Command", "Select-String 'version:' | ForEach-Object { $_.Matches.Groups[1].Value }")
}
var out1 bytes.Buffer
var out2 bytes.Buffer
cmd1.Stdout = &out1
cmd2.Stdin = &out1
cmd2.Stdout = &out2
if err := cmd1.Run(); err != nil {
fmt.Println("Chart does not exist in the specified path")
return "", err
}
if err := cmd2.Run(); err != nil {
return "", err
}
return out2.String(), nil
}
-73
View File
@@ -1,73 +0,0 @@
# Plugin Name: Push OCI Chart to Registry
# Description: Pushes an Helm Chart to a Docker Registry
import os
import subprocess
# Environment Variables
def main_function():
CHART_NAME = os.getenv("PLUGIN_CHART_NAME")
CHART_VERSION = os.getenv("PLUGIN_CHART_VERSION", "1.0.0")
DOCKER_REGISTRY = os.getenv(
"PLUGIN_DOCKER_REGISTRY", 'registry.hub.docker.com')
DOCKER_USERNAME = os.getenv(
"PLUGIN_DOCKER_USERNAME")
DOCKER_PASSWORD = os.getenv(
"PLUGIN_DOCKER_PASSWORD")
CHART_PATH = os.getenv("PLUGIN_CHART_PATH")
DOCKER_NAMESPACE = os.getenv("PLUGIN_DOCKER_NAMESPACE")
if (CHART_NAME is None or CHART_NAME == ""):
print("Please provide a chart name")
exit(1)
if (DOCKER_USERNAME is None or DOCKER_PASSWORD is None or DOCKER_USERNAME == "" or DOCKER_PASSWORD == ""):
print("Please provide a username and a password")
exit(1)
if (DOCKER_NAMESPACE is None or DOCKER_NAMESPACE == ""):
print("Please provide a namespace")
exit(1)
if (CHART_PATH is not None):
os.chdir(CHART_PATH)
try:
if (subprocess.run(["helm", "package", "--dependency-update", "."]).returncode != 0):
raise Exception("Failed to package chart!")
except:
print("Failed to package chart!")
exit(1)
chart_filename = f"{CHART_NAME}-{CHART_VERSION}.tgz"
try:
login_command = ['helm', 'registry', 'login', DOCKER_REGISTRY,
'-u', DOCKER_USERNAME, '-p', DOCKER_PASSWORD]
if (subprocess.run(login_command).returncode != 0):
raise Exception("Failed to login!")
except:
print("Failed to login!")
exit(1)
try:
docker_push_command = ["helm", "push", chart_filename,
f"oci://{DOCKER_REGISTRY}/{DOCKER_NAMESPACE}"]
if (subprocess.run(docker_push_command).returncode != 0):
raise Exception("Failed to push chart!")
else:
print("Chart pushed successfully.")
except:
print("Failed to push chart!")
exit(1)
if __name__ == "__main__":
main_function()
-97
View File
@@ -1,97 +0,0 @@
import unittest
from unittest.mock import patch
import os
from io import StringIO
from main import main_function
class TestPushOCIChartToRegistry(unittest.TestCase):
def tearDown(self):
os.environ["PLUGIN_CHART_NAME"] = ""
os.environ["PLUGIN_CHART_VERSION"] = ""
os.environ["PLUGIN_DOCKER_REGISTRY"] = ""
os.environ["PLUGIN_DOCKER_USERNAME"] = ""
os.environ["PLUGIN_DOCKER_PASSWORD"] = ""
os.environ["PLUGIN_CHART_PATH"] = ""
@patch("subprocess.run")
def test_successful_chart_push(self, mock_subprocess_run):
os.environ["PLUGIN_CHART_NAME"] = "mywebapp"
test_docker_username = os.environ["TEST_DOCKER_USERNAME"]
test_docker_password = os.environ["TEST_DOCKER_PASSWORD"]
os.environ["PLUGIN_DOCKER_USERNAME"] = test_docker_username
os.environ["PLUGIN_DOCKER_PASSWORD"] = test_docker_password
os.environ["PLUGIN_CHART_VERSION"] = "5.0.0"
os.environ["PLUGIN_DOCKER_REGISTRY"] = "registry.hub.docker.com"
os.environ["PLUGIN_CHART_PATH"] = "."
mock_subprocess_run.return_value.returncode = 0
with patch("sys.stdout", new_callable=StringIO) as mock_stdout:
main_function()
mock_subprocess_run.assert_called_with(
["helm", "push", "mywebapp-5.0.0.tgz", f"oci://registry.hub.docker.com/{test_docker_username}"])
expected_output = 'Chart pushed successfully.'
self.assertEqual(mock_stdout.getvalue().strip(), expected_output)
@patch("subprocess.run")
def test_failed_to_package_chart(self, mock_subprocess_run):
os.environ["PLUGIN_CHART_NAME"] = "mywebapp"
test_docker_username = os.environ["TEST_DOCKER_USERNAME"]
test_docker_password = os.environ["TEST_DOCKER_PASSWORD"]
os.environ["PLUGIN_DOCKER_USERNAME"] = test_docker_username
os.environ["PLUGIN_DOCKER_PASSWORD"] = test_docker_password
os.environ["PLUGIN_CHART_PATH"] = "chart"
mock_subprocess_run.return_value.returncode = 1
with patch("sys.stdout", new_callable=StringIO) as mock_stdout:
with self.assertRaises(SystemExit) as context:
main_function()
self.assertEqual(context.exception.code, 1)
expected_output = 'Failed to package chart!'
self.assertEqual(mock_stdout.getvalue().strip(), expected_output)
@patch("subprocess.run")
def test_chart_name_not_provided(self, mock_subprocess_run):
test_docker_username = os.environ["TEST_DOCKER_USERNAME"]
test_docker_password = os.environ["TEST_DOCKER_PASSWORD"]
os.environ["PLUGIN_DOCKER_USERNAME"] = test_docker_username
os.environ["PLUGIN_DOCKER_PASSWORD"] = test_docker_password
os.environ["PLUGIN_CHART_PATH"] = "chart"
mock_subprocess_run.return_value.returncode = 0
with patch("sys.stdout", new_callable=StringIO) as mock_stdout:
with self.assertRaises(SystemExit) as context:
main_function()
self.assertEqual(context.exception.code, 1)
expected_output = 'Please provide a chart name'
self.assertEqual(mock_stdout.getvalue().strip(), expected_output)
@patch("subprocess.run")
def test_username_and_password_not_provided(self, mock_subprocess_run):
os.environ["PLUGIN_CHART_NAME"] = "mywebapp"
os.environ["PLUGIN_CHART_PATH"] = "chart"
mock_subprocess_run.return_value.returncode = 0
with patch("sys.stdout", new_callable=StringIO) as mock_stdout:
with self.assertRaises(SystemExit) as context:
main_function()
self.assertEqual(context.exception.code, 1)
expected_output = 'Please provide a username and a password'
self.assertEqual(mock_stdout.getvalue().strip(), expected_output)
if __name__ == '__main__':
unittest.main()