Skip to content

Commit 0138c52

Browse files
authored
Support Helm package manager (#125)
Signed-off-by: Jiyeong Seok <[email protected]>
1 parent e0d8bfb commit 0138c52

File tree

15 files changed

+499
-3
lines changed

15 files changed

+499
-3
lines changed

.reuse/dep5

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,4 +104,8 @@ License: Apache-2.0
104104

105105
Files: tests/test_nuget2/*
106106
Copyright: 2022 LG Electronics
107-
License: Apache-2.0
107+
License: Apache-2.0
108+
109+
Files: tests/test_helm/*
110+
Copyright: 2023 LG Electronics
111+
License: Apache-2.0

src/fosslight_dependency/_analyze_dependency.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
from fosslight_dependency.package_manager.Carthage import Carthage
1818
from fosslight_dependency.package_manager.Go import Go
1919
from fosslight_dependency.package_manager.Nuget import Nuget
20+
from fosslight_dependency.package_manager.Helm import Helm
2021
import fosslight_util.constant as constant
2122

2223
logger = logging.getLogger(constant.LOGGER_NAME)
@@ -50,6 +51,8 @@ def analyze_dependency(package_manager_name, input_dir, output_dir, pip_activate
5051
package_manager = Go(input_dir, output_dir)
5152
elif package_manager_name == const.NUGET:
5253
package_manager = Nuget(input_dir, output_dir)
54+
elif package_manager_name == const.HELM:
55+
package_manager = Helm(input_dir, output_dir)
5356
else:
5457
logger.error(f"Not supported package manager name: {package_manager_name}")
5558
ret = False

src/fosslight_dependency/constant.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
CARTHAGE = 'carthage'
2222
GO = 'go'
2323
NUGET = 'nuget'
24+
HELM = 'helm'
2425

2526
# Supported package name and manifest file
2627
SUPPORT_PACKAE = {
@@ -34,7 +35,8 @@
3435
SWIFT: 'Package.resolved',
3536
CARTHAGE: 'Cartfile.resolved',
3637
GO: 'go.mod',
37-
NUGET: ['packages.config', os.path.join('obj', 'project.assets.json')]
38+
NUGET: ['packages.config', os.path.join('obj', 'project.assets.json')],
39+
HELM: 'Chart.yaml'
3840
}
3941

4042
# default android app name
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
#!/usr/bin/env python
2+
# -*- coding: utf-8 -*-
3+
# Copyright (c) 2023 LG Electronics Inc.
4+
# SPDX-License-Identifier: Apache-2.0
5+
6+
import os
7+
import logging
8+
import subprocess
9+
import yaml
10+
import shutil
11+
import fosslight_util.constant as constant
12+
import fosslight_dependency.constant as const
13+
from fosslight_dependency._package_manager import PackageManager
14+
from fosslight_util.download import extract_compressed_dir
15+
16+
logger = logging.getLogger(constant.LOGGER_NAME)
17+
18+
19+
class Helm(PackageManager):
20+
package_manager_name = const.HELM
21+
tmp_charts_dir = 'tmp_charts'
22+
23+
input_file_name = const.SUPPORT_PACKAE.get(package_manager_name)
24+
25+
def __init__(self, input_dir, output_dir):
26+
super().__init__(self.package_manager_name, '', input_dir, output_dir)
27+
self.append_input_package_list_file(self.input_file_name)
28+
29+
def __del__(self):
30+
if os.path.exists(self.tmp_charts_dir):
31+
shutil.rmtree(self.tmp_charts_dir, ignore_errors=True)
32+
33+
def run_plugin(self):
34+
ret = True
35+
charts_dir = 'charts'
36+
if os.path.isdir(charts_dir):
37+
shutil.copytree(charts_dir, self.tmp_charts_dir)
38+
else:
39+
logger.info("Execute 'helm dependency build' to obtain package info.")
40+
cmd = "helm dependency build"
41+
42+
ret_cmd = subprocess.call(cmd, shell=True)
43+
if ret_cmd != 0:
44+
logger.error(f"Failed to build helm dependency: {cmd}")
45+
ret = False
46+
else:
47+
shutil.copytree(charts_dir, self.tmp_charts_dir)
48+
shutil.rmtree(charts_dir, ignore_errors=True)
49+
50+
ret = extract_compressed_dir(self.tmp_charts_dir, self.tmp_charts_dir, False)
51+
if not ret:
52+
logger.error(f'Fail to extract compressed dir: {self.tmp_charts_dir}')
53+
else:
54+
logger.warning('Success to extract compressed dir')
55+
56+
return ret
57+
58+
def parse_oss_information(self, f_name):
59+
dep_item_list = []
60+
sheet_list = []
61+
62+
with open(f_name, 'r', encoding='utf8') as yaml_fp:
63+
yaml_f = yaml.safe_load(yaml_fp)
64+
for dep in yaml_f['dependencies']:
65+
dep_item_list.append(dep['name'])
66+
for dep in dep_item_list:
67+
try:
68+
f_path = os.path.join(self.tmp_charts_dir, dep, f_name)
69+
with open(f_path, 'r', encoding='utf8') as yaml_fp:
70+
yaml_f = yaml.safe_load(yaml_fp)
71+
oss_name = f'{self.package_manager_name}:{yaml_f["name"]}'
72+
oss_version = yaml_f.get('version', '')
73+
if oss_version.startswith('v'):
74+
oss_version = oss_version[1:]
75+
76+
homepage = yaml_f.get('home', '')
77+
dn_loc = ''
78+
if yaml_f.get('sources', '') != '':
79+
dn_loc = yaml_f.get('sources', '')[0]
80+
81+
license_name = ''
82+
if yaml_f.get('annotations', '') != '':
83+
license_name = yaml_f['annotations'].get('licenses', '')
84+
85+
if self.direct_dep:
86+
comment = 'direct'
87+
88+
except Exception as e:
89+
logging.warning(f"Fail to parse chart info {dep}: {e}")
90+
continue
91+
92+
sheet_list.append([const.SUPPORT_PACKAE.get(self.package_manager_name),
93+
oss_name, oss_version, license_name, dn_loc, homepage, '', '', comment])
94+
95+
return sheet_list

tests/test_helm/Chart.yaml

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
name: helm_test
2+
description: A Helm chart for Kubernetes
3+
dependencies:
4+
- condition: memcached.enabled
5+
name: memcached
6+
repository: https://charts.bitnami.com/bitnami
7+
version: 6.x.x
8+
- condition: mariadb.enabled
9+
name: mariadb
10+
repository: https://charts.bitnami.com/bitnami
11+
version: 11.x.x
12+
- name: cert-manager
13+
version: 1.11.0
14+
repository: https://charts.jetstack.io
15+
# A chart can be either an 'application' or a 'library' chart.
16+
#
17+
# Application charts are a collection of templates that can be packaged into versioned archives
18+
# to be deployed.
19+
#
20+
# Library charts provide useful utilities or functions for the chart developer. They're included as
21+
# a dependency of application charts to inject those utilities and functions into the rendering
22+
# pipeline. Library charts do not define any templates and therefore cannot be deployed.
23+
type: application
24+
25+
# This is the chart version. This version number should be incremented each time you make changes
26+
# to the chart and its templates, including the app version.
27+
# Versions are expected to follow Semantic Versioning (https://semver.org/)
28+
version: 0.1.0
29+
30+
# This is the version number of the application being deployed. This version number should be
31+
# incremented each time you make changes to the application. Versions are not expected to
32+
# follow Semantic Versioning. They should reflect the version the application is using.
33+
# It is recommended to use it with quotes.
34+
appVersion: "1.16.0"

tests/test_helm/templates/NOTES.txt

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
1. Get the application URL by running these commands:
2+
{{- if .Values.ingress.enabled }}
3+
{{- range $host := .Values.ingress.hosts }}
4+
{{- range .paths }}
5+
http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ .path }}
6+
{{- end }}
7+
{{- end }}
8+
{{- else if contains "NodePort" .Values.service.type }}
9+
export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "helm_test.fullname" . }})
10+
export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
11+
echo http://$NODE_IP:$NODE_PORT
12+
{{- else if contains "LoadBalancer" .Values.service.type }}
13+
NOTE: It may take a few minutes for the LoadBalancer IP to be available.
14+
You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "helm_test.fullname" . }}'
15+
export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "helm_test.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}")
16+
echo http://$SERVICE_IP:{{ .Values.service.port }}
17+
{{- else if contains "ClusterIP" .Values.service.type }}
18+
export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "helm_test.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
19+
export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}")
20+
echo "Visit http://127.0.0.1:8080 to use your application"
21+
kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT
22+
{{- end }}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
{{/*
2+
Expand the name of the chart.
3+
*/}}
4+
{{- define "helm_test.name" -}}
5+
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
6+
{{- end }}
7+
8+
{{/*
9+
Create a default fully qualified app name.
10+
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
11+
If release name contains chart name it will be used as a full name.
12+
*/}}
13+
{{- define "helm_test.fullname" -}}
14+
{{- if .Values.fullnameOverride }}
15+
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
16+
{{- else }}
17+
{{- $name := default .Chart.Name .Values.nameOverride }}
18+
{{- if contains $name .Release.Name }}
19+
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
20+
{{- else }}
21+
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
22+
{{- end }}
23+
{{- end }}
24+
{{- end }}
25+
26+
{{/*
27+
Create chart name and version as used by the chart label.
28+
*/}}
29+
{{- define "helm_test.chart" -}}
30+
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
31+
{{- end }}
32+
33+
{{/*
34+
Common labels
35+
*/}}
36+
{{- define "helm_test.labels" -}}
37+
helm.sh/chart: {{ include "helm_test.chart" . }}
38+
{{ include "helm_test.selectorLabels" . }}
39+
{{- if .Chart.AppVersion }}
40+
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
41+
{{- end }}
42+
app.kubernetes.io/managed-by: {{ .Release.Service }}
43+
{{- end }}
44+
45+
{{/*
46+
Selector labels
47+
*/}}
48+
{{- define "helm_test.selectorLabels" -}}
49+
app.kubernetes.io/name: {{ include "helm_test.name" . }}
50+
app.kubernetes.io/instance: {{ .Release.Name }}
51+
{{- end }}
52+
53+
{{/*
54+
Create the name of the service account to use
55+
*/}}
56+
{{- define "helm_test.serviceAccountName" -}}
57+
{{- if .Values.serviceAccount.create }}
58+
{{- default (include "helm_test.fullname" .) .Values.serviceAccount.name }}
59+
{{- else }}
60+
{{- default "default" .Values.serviceAccount.name }}
61+
{{- end }}
62+
{{- end }}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
apiVersion: apps/v1
2+
kind: Deployment
3+
metadata:
4+
name: {{ include "helm_test.fullname" . }}
5+
labels:
6+
{{- include "helm_test.labels" . | nindent 4 }}
7+
spec:
8+
{{- if not .Values.autoscaling.enabled }}
9+
replicas: {{ .Values.replicaCount }}
10+
{{- end }}
11+
selector:
12+
matchLabels:
13+
{{- include "helm_test.selectorLabels" . | nindent 6 }}
14+
template:
15+
metadata:
16+
{{- with .Values.podAnnotations }}
17+
annotations:
18+
{{- toYaml . | nindent 8 }}
19+
{{- end }}
20+
labels:
21+
{{- include "helm_test.selectorLabels" . | nindent 8 }}
22+
spec:
23+
{{- with .Values.imagePullSecrets }}
24+
imagePullSecrets:
25+
{{- toYaml . | nindent 8 }}
26+
{{- end }}
27+
serviceAccountName: {{ include "helm_test.serviceAccountName" . }}
28+
securityContext:
29+
{{- toYaml .Values.podSecurityContext | nindent 8 }}
30+
containers:
31+
- name: {{ .Chart.Name }}
32+
securityContext:
33+
{{- toYaml .Values.securityContext | nindent 12 }}
34+
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
35+
imagePullPolicy: {{ .Values.image.pullPolicy }}
36+
ports:
37+
- name: http
38+
containerPort: {{ .Values.service.port }}
39+
protocol: TCP
40+
livenessProbe:
41+
httpGet:
42+
path: /
43+
port: http
44+
readinessProbe:
45+
httpGet:
46+
path: /
47+
port: http
48+
resources:
49+
{{- toYaml .Values.resources | nindent 12 }}
50+
{{- with .Values.nodeSelector }}
51+
nodeSelector:
52+
{{- toYaml . | nindent 8 }}
53+
{{- end }}
54+
{{- with .Values.affinity }}
55+
affinity:
56+
{{- toYaml . | nindent 8 }}
57+
{{- end }}
58+
{{- with .Values.tolerations }}
59+
tolerations:
60+
{{- toYaml . | nindent 8 }}
61+
{{- end }}

tests/test_helm/templates/hpa.yaml

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
{{- if .Values.autoscaling.enabled }}
2+
apiVersion: autoscaling/v2beta1
3+
kind: HorizontalPodAutoscaler
4+
metadata:
5+
name: {{ include "helm_test.fullname" . }}
6+
labels:
7+
{{- include "helm_test.labels" . | nindent 4 }}
8+
spec:
9+
scaleTargetRef:
10+
apiVersion: apps/v1
11+
kind: Deployment
12+
name: {{ include "helm_test.fullname" . }}
13+
minReplicas: {{ .Values.autoscaling.minReplicas }}
14+
maxReplicas: {{ .Values.autoscaling.maxReplicas }}
15+
metrics:
16+
{{- if .Values.autoscaling.targetCPUUtilizationPercentage }}
17+
- type: Resource
18+
resource:
19+
name: cpu
20+
targetAverageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }}
21+
{{- end }}
22+
{{- if .Values.autoscaling.targetMemoryUtilizationPercentage }}
23+
- type: Resource
24+
resource:
25+
name: memory
26+
targetAverageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }}
27+
{{- end }}
28+
{{- end }}

0 commit comments

Comments
 (0)