mirror of https://github.com/helm/helm
commit
16229f8014
@ -0,0 +1,32 @@
|
||||
---
|
||||
# Source: issue-9027/charts/subchart/templates/values.yaml
|
||||
global:
|
||||
hash:
|
||||
key3: 13
|
||||
key4: 4
|
||||
key5: 5
|
||||
key6: 6
|
||||
hash:
|
||||
key3: 13
|
||||
key4: 4
|
||||
key5: 5
|
||||
key6: 6
|
||||
---
|
||||
# Source: issue-9027/templates/values.yaml
|
||||
global:
|
||||
hash:
|
||||
key1: null
|
||||
key2: null
|
||||
key3: 13
|
||||
subchart:
|
||||
global:
|
||||
hash:
|
||||
key3: 13
|
||||
key4: 4
|
||||
key5: 5
|
||||
key6: 6
|
||||
hash:
|
||||
key3: 13
|
||||
key4: 4
|
||||
key5: 5
|
||||
key6: 6
|
@ -0,0 +1,122 @@
|
||||
---
|
||||
# Source: subchart/templates/subdir/serviceaccount.yaml
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: subchart-sa
|
||||
---
|
||||
# Source: subchart/templates/subdir/configmap.yaml
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: subchart-cm
|
||||
data:
|
||||
value: qux
|
||||
---
|
||||
# Source: subchart/templates/subdir/role.yaml
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: Role
|
||||
metadata:
|
||||
name: subchart-role
|
||||
rules:
|
||||
- apiGroups: [""]
|
||||
resources: ["pods"]
|
||||
verbs: ["get","list","watch"]
|
||||
---
|
||||
# Source: subchart/templates/subdir/rolebinding.yaml
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: RoleBinding
|
||||
metadata:
|
||||
name: subchart-binding
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: Role
|
||||
name: subchart-role
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: subchart-sa
|
||||
namespace: default
|
||||
---
|
||||
# Source: subchart/charts/subcharta/templates/service.yaml
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: subcharta
|
||||
labels:
|
||||
helm.sh/chart: "subcharta-0.1.0"
|
||||
spec:
|
||||
type: ClusterIP
|
||||
ports:
|
||||
- port: 80
|
||||
targetPort: 80
|
||||
protocol: TCP
|
||||
name: apache
|
||||
selector:
|
||||
app.kubernetes.io/name: subcharta
|
||||
---
|
||||
# Source: subchart/charts/subchartb/templates/service.yaml
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: subchartb
|
||||
labels:
|
||||
helm.sh/chart: "subchartb-0.1.0"
|
||||
spec:
|
||||
type: ClusterIP
|
||||
ports:
|
||||
- port: 80
|
||||
targetPort: 80
|
||||
protocol: TCP
|
||||
name: nginx
|
||||
selector:
|
||||
app.kubernetes.io/name: subchartb
|
||||
---
|
||||
# Source: subchart/templates/service.yaml
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: subchart
|
||||
labels:
|
||||
helm.sh/chart: "subchart-0.1.0"
|
||||
app.kubernetes.io/instance: "release-name"
|
||||
kube-version/major: "1"
|
||||
kube-version/minor: "20"
|
||||
kube-version/version: "v1.20.0"
|
||||
spec:
|
||||
type: ClusterIP
|
||||
ports:
|
||||
- port: 80
|
||||
targetPort: 80
|
||||
protocol: TCP
|
||||
name: nginx
|
||||
selector:
|
||||
app.kubernetes.io/name: subchart
|
||||
---
|
||||
# Source: subchart/templates/tests/test-config.yaml
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: "release-name-testconfig"
|
||||
annotations:
|
||||
"helm.sh/hook": test
|
||||
data:
|
||||
message: Hello World
|
||||
---
|
||||
# Source: subchart/templates/tests/test-nothing.yaml
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: "release-name-test"
|
||||
annotations:
|
||||
"helm.sh/hook": test
|
||||
spec:
|
||||
containers:
|
||||
- name: test
|
||||
image: "alpine:latest"
|
||||
envFrom:
|
||||
- configMapRef:
|
||||
name: "release-name-testconfig"
|
||||
command:
|
||||
- echo
|
||||
- "$message"
|
||||
restartPolicy: Never
|
@ -0,0 +1,122 @@
|
||||
---
|
||||
# Source: subchart/templates/subdir/serviceaccount.yaml
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: subchart-sa
|
||||
---
|
||||
# Source: subchart/templates/subdir/configmap.yaml
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: subchart-cm
|
||||
data:
|
||||
value: baz
|
||||
---
|
||||
# Source: subchart/templates/subdir/role.yaml
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: Role
|
||||
metadata:
|
||||
name: subchart-role
|
||||
rules:
|
||||
- apiGroups: [""]
|
||||
resources: ["pods"]
|
||||
verbs: ["get","list","watch"]
|
||||
---
|
||||
# Source: subchart/templates/subdir/rolebinding.yaml
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: RoleBinding
|
||||
metadata:
|
||||
name: subchart-binding
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: Role
|
||||
name: subchart-role
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: subchart-sa
|
||||
namespace: default
|
||||
---
|
||||
# Source: subchart/charts/subcharta/templates/service.yaml
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: subcharta
|
||||
labels:
|
||||
helm.sh/chart: "subcharta-0.1.0"
|
||||
spec:
|
||||
type: ClusterIP
|
||||
ports:
|
||||
- port: 80
|
||||
targetPort: 80
|
||||
protocol: TCP
|
||||
name: apache
|
||||
selector:
|
||||
app.kubernetes.io/name: subcharta
|
||||
---
|
||||
# Source: subchart/charts/subchartb/templates/service.yaml
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: subchartb
|
||||
labels:
|
||||
helm.sh/chart: "subchartb-0.1.0"
|
||||
spec:
|
||||
type: ClusterIP
|
||||
ports:
|
||||
- port: 80
|
||||
targetPort: 80
|
||||
protocol: TCP
|
||||
name: nginx
|
||||
selector:
|
||||
app.kubernetes.io/name: subchartb
|
||||
---
|
||||
# Source: subchart/templates/service.yaml
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: subchart
|
||||
labels:
|
||||
helm.sh/chart: "subchart-0.1.0"
|
||||
app.kubernetes.io/instance: "release-name"
|
||||
kube-version/major: "1"
|
||||
kube-version/minor: "20"
|
||||
kube-version/version: "v1.20.0"
|
||||
spec:
|
||||
type: ClusterIP
|
||||
ports:
|
||||
- port: 80
|
||||
targetPort: 80
|
||||
protocol: TCP
|
||||
name: nginx
|
||||
selector:
|
||||
app.kubernetes.io/name: subchart
|
||||
---
|
||||
# Source: subchart/templates/tests/test-config.yaml
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: "release-name-testconfig"
|
||||
annotations:
|
||||
"helm.sh/hook": test
|
||||
data:
|
||||
message: Hello World
|
||||
---
|
||||
# Source: subchart/templates/tests/test-nothing.yaml
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: "release-name-test"
|
||||
annotations:
|
||||
"helm.sh/hook": test
|
||||
spec:
|
||||
containers:
|
||||
- name: test
|
||||
image: "alpine:latest"
|
||||
envFrom:
|
||||
- configMapRef:
|
||||
name: "release-name-testconfig"
|
||||
command:
|
||||
- echo
|
||||
- "$message"
|
||||
restartPolicy: Never
|
@ -0,0 +1,122 @@
|
||||
---
|
||||
# Source: subchart/templates/subdir/serviceaccount.yaml
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: subchart-sa
|
||||
---
|
||||
# Source: subchart/templates/subdir/configmap.yaml
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: subchart-cm
|
||||
data:
|
||||
value: bar
|
||||
---
|
||||
# Source: subchart/templates/subdir/role.yaml
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: Role
|
||||
metadata:
|
||||
name: subchart-role
|
||||
rules:
|
||||
- apiGroups: [""]
|
||||
resources: ["pods"]
|
||||
verbs: ["get","list","watch"]
|
||||
---
|
||||
# Source: subchart/templates/subdir/rolebinding.yaml
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: RoleBinding
|
||||
metadata:
|
||||
name: subchart-binding
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: Role
|
||||
name: subchart-role
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: subchart-sa
|
||||
namespace: default
|
||||
---
|
||||
# Source: subchart/charts/subcharta/templates/service.yaml
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: subcharta
|
||||
labels:
|
||||
helm.sh/chart: "subcharta-0.1.0"
|
||||
spec:
|
||||
type: ClusterIP
|
||||
ports:
|
||||
- port: 80
|
||||
targetPort: 80
|
||||
protocol: TCP
|
||||
name: apache
|
||||
selector:
|
||||
app.kubernetes.io/name: subcharta
|
||||
---
|
||||
# Source: subchart/charts/subchartb/templates/service.yaml
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: subchartb
|
||||
labels:
|
||||
helm.sh/chart: "subchartb-0.1.0"
|
||||
spec:
|
||||
type: ClusterIP
|
||||
ports:
|
||||
- port: 80
|
||||
targetPort: 80
|
||||
protocol: TCP
|
||||
name: nginx
|
||||
selector:
|
||||
app.kubernetes.io/name: subchartb
|
||||
---
|
||||
# Source: subchart/templates/service.yaml
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: subchart
|
||||
labels:
|
||||
helm.sh/chart: "subchart-0.1.0"
|
||||
app.kubernetes.io/instance: "release-name"
|
||||
kube-version/major: "1"
|
||||
kube-version/minor: "20"
|
||||
kube-version/version: "v1.20.0"
|
||||
spec:
|
||||
type: ClusterIP
|
||||
ports:
|
||||
- port: 80
|
||||
targetPort: 80
|
||||
protocol: TCP
|
||||
name: nginx
|
||||
selector:
|
||||
app.kubernetes.io/name: subchart
|
||||
---
|
||||
# Source: subchart/templates/tests/test-config.yaml
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: "release-name-testconfig"
|
||||
annotations:
|
||||
"helm.sh/hook": test
|
||||
data:
|
||||
message: Hello World
|
||||
---
|
||||
# Source: subchart/templates/tests/test-nothing.yaml
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: "release-name-test"
|
||||
annotations:
|
||||
"helm.sh/hook": test
|
||||
spec:
|
||||
containers:
|
||||
- name: test
|
||||
image: "alpine:latest"
|
||||
envFrom:
|
||||
- configMapRef:
|
||||
name: "release-name-testconfig"
|
||||
command:
|
||||
- echo
|
||||
- "$message"
|
||||
restartPolicy: Never
|
@ -1 +1 @@
|
||||
version.BuildInfo{Version:"v3.11", GitCommit:"", GitTreeState:"", GoVersion:""}
|
||||
version.BuildInfo{Version:"v3.12", GitCommit:"", GitTreeState:"", GoVersion:""}
|
||||
|
@ -1 +1 @@
|
||||
version.BuildInfo{Version:"v3.11", GitCommit:"", GitTreeState:"", GoVersion:""}
|
||||
version.BuildInfo{Version:"v3.12", GitCommit:"", GitTreeState:"", GoVersion:""}
|
||||
|
@ -1 +1 @@
|
||||
v3.11
|
||||
v3.12
|
||||
|
@ -1 +1 @@
|
||||
Version: v3.11
|
||||
Version: v3.12
|
@ -1 +1 @@
|
||||
version.BuildInfo{Version:"v3.11", GitCommit:"", GitTreeState:"", GoVersion:""}
|
||||
version.BuildInfo{Version:"v3.12", GitCommit:"", GitTreeState:"", GoVersion:""}
|
||||
|
@ -0,0 +1,6 @@
|
||||
apiVersion: v2
|
||||
name: issue-9027
|
||||
version: 0.1.0
|
||||
dependencies:
|
||||
- name: subchart
|
||||
version: 0.1.0
|
@ -0,0 +1,3 @@
|
||||
apiVersion: v2
|
||||
name: subchart
|
||||
version: 0.1.0
|
@ -0,0 +1 @@
|
||||
{{ .Values | toYaml }}
|
@ -0,0 +1,17 @@
|
||||
global:
|
||||
hash:
|
||||
key1: 1
|
||||
key2: 2
|
||||
key3: 3
|
||||
key4: 4
|
||||
key5: 5
|
||||
key6: 6
|
||||
|
||||
|
||||
hash:
|
||||
key1: 1
|
||||
key2: 2
|
||||
key3: 3
|
||||
key4: 4
|
||||
key5: 5
|
||||
key6: 6
|
@ -0,0 +1 @@
|
||||
{{ .Values | toYaml }}
|
@ -0,0 +1,11 @@
|
||||
global:
|
||||
hash:
|
||||
key1: null
|
||||
key2: null
|
||||
key3: 13
|
||||
|
||||
subchart:
|
||||
hash:
|
||||
key1: null
|
||||
key2: null
|
||||
key3: 13
|
@ -0,0 +1,5 @@
|
||||
# This file is used to test values passed by file at the command line
|
||||
|
||||
configmap:
|
||||
enabled: true
|
||||
value: "qux"
|
@ -0,0 +1,8 @@
|
||||
{{ if .Values.configmap.enabled -}}
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: {{ .Chart.Name }}-cm
|
||||
data:
|
||||
value: {{ .Values.configmap.value }}
|
||||
{{- end }}
|
@ -0,0 +1,11 @@
|
||||
apiVersion: v2
|
||||
name: app3
|
||||
description: A Helm chart for Kubernetes
|
||||
type: application
|
||||
version: 0.1.0
|
||||
|
||||
dependencies:
|
||||
- name: library
|
||||
version: 0.1.0
|
||||
import-values:
|
||||
- defaults
|
@ -0,0 +1,5 @@
|
||||
apiVersion: v2
|
||||
name: library
|
||||
description: A Helm chart for Kubernetes
|
||||
type: library
|
||||
version: 0.1.0
|
@ -0,0 +1,9 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
spec:
|
||||
type: {{ .Values.service.type }}
|
||||
ports:
|
||||
- port: {{ .Values.service.port }}
|
||||
targetPort: http
|
||||
protocol: TCP
|
||||
name: http
|
@ -0,0 +1,5 @@
|
||||
exports:
|
||||
defaults:
|
||||
service:
|
||||
type: ClusterIP
|
||||
port: 9090
|
@ -0,0 +1 @@
|
||||
{{- include "library.service" . }}
|
@ -0,0 +1,2 @@
|
||||
service:
|
||||
type: ClusterIP
|
@ -0,0 +1,9 @@
|
||||
apiVersion: v2
|
||||
name: app4
|
||||
description: A Helm chart for Kubernetes
|
||||
type: application
|
||||
version: 0.1.0
|
||||
|
||||
dependencies:
|
||||
- name: library
|
||||
version: 0.1.0
|
@ -0,0 +1,5 @@
|
||||
apiVersion: v2
|
||||
name: library
|
||||
description: A Helm chart for Kubernetes
|
||||
type: library
|
||||
version: 0.1.0
|
@ -0,0 +1,9 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
spec:
|
||||
type: {{ .Values.service.type }}
|
||||
ports:
|
||||
- port: {{ .Values.service.port }}
|
||||
targetPort: http
|
||||
protocol: TCP
|
||||
name: http
|
@ -0,0 +1,5 @@
|
||||
exports:
|
||||
defaults:
|
||||
service:
|
||||
type: ClusterIP
|
||||
port: 9090
|
@ -0,0 +1 @@
|
||||
{{- include "library.service" . }}
|
@ -0,0 +1,3 @@
|
||||
service:
|
||||
type: ClusterIP
|
||||
port: 1234
|
@ -0,0 +1,23 @@
|
||||
# Patterns to ignore when building packages.
|
||||
# This supports shell glob matching, relative path matching, and
|
||||
# negation (prefixed with !). Only one pattern per line.
|
||||
.DS_Store
|
||||
# Common VCS dirs
|
||||
.git/
|
||||
.gitignore
|
||||
.bzr/
|
||||
.bzrignore
|
||||
.hg/
|
||||
.hgignore
|
||||
.svn/
|
||||
# Common backup files
|
||||
*.swp
|
||||
*.bak
|
||||
*.tmp
|
||||
*.orig
|
||||
*~
|
||||
# Various IDEs
|
||||
.project
|
||||
.idea/
|
||||
*.tmproj
|
||||
.vscode/
|
@ -0,0 +1,25 @@
|
||||
apiVersion: v2
|
||||
name: test
|
||||
description: A Helm chart for Kubernetes
|
||||
|
||||
# A chart can be either an 'application' or a 'library' chart.
|
||||
#
|
||||
# Application charts are a collection of templates that can be packaged into versioned archives
|
||||
# to be deployed.
|
||||
#
|
||||
# Library charts provide useful utilities or functions for the chart developer. They're included as
|
||||
# a dependency of application charts to inject those utilities and functions into the rendering
|
||||
# pipeline. Library charts do not define any templates and therefore cannot be deployed.
|
||||
type: application
|
||||
|
||||
# This is the chart version. This version number should be incremented each time you make changes
|
||||
# to the chart and its templates, including the app version.
|
||||
# Versions are expected to follow Semantic Versioning (https://semver.org/)
|
||||
version: 0.1.0
|
||||
|
||||
# This is the version number of the application being deployed. This version number should be
|
||||
# incremented each time you make changes to the application. Versions are not expected to
|
||||
# follow Semantic Versioning. They should reflect the version the application is using.
|
||||
# It is recommended to use it with quotes.
|
||||
appVersion: "1.16.0"
|
||||
icon: https://riverrun.io
|
@ -0,0 +1 @@
|
||||
{ {- $relname := .Release.Name -}}
|
@ -0,0 +1,82 @@
|
||||
# Default values for test.
|
||||
# This is a YAML-formatted file.
|
||||
# Declare variables to be passed into your templates.
|
||||
|
||||
replicaCount: 1
|
||||
|
||||
image:
|
||||
repository: nginx
|
||||
pullPolicy: IfNotPresent
|
||||
# Overrides the image tag whose default is the chart appVersion.
|
||||
tag: ""
|
||||
|
||||
imagePullSecrets: []
|
||||
nameOverride: ""
|
||||
fullnameOverride: ""
|
||||
|
||||
serviceAccount:
|
||||
# Specifies whether a service account should be created
|
||||
create: true
|
||||
# Annotations to add to the service account
|
||||
annotations: {}
|
||||
# The name of the service account to use.
|
||||
# If not set and create is true, a name is generated using the fullname template
|
||||
name: ""
|
||||
|
||||
podAnnotations: {}
|
||||
|
||||
podSecurityContext: {}
|
||||
# fsGroup: 2000
|
||||
|
||||
securityContext: {}
|
||||
# capabilities:
|
||||
# drop:
|
||||
# - ALL
|
||||
# readOnlyRootFilesystem: true
|
||||
# runAsNonRoot: true
|
||||
# runAsUser: 1000
|
||||
|
||||
service:
|
||||
type: ClusterIP
|
||||
port: 80
|
||||
|
||||
ingress:
|
||||
enabled: false
|
||||
className: ""
|
||||
annotations: {}
|
||||
# kubernetes.io/ingress.class: nginx
|
||||
# kubernetes.io/tls-acme: "true"
|
||||
hosts:
|
||||
- host: chart-example.local
|
||||
paths:
|
||||
- path: /
|
||||
pathType: ImplementationSpecific
|
||||
tls: []
|
||||
# - secretName: chart-example-tls
|
||||
# hosts:
|
||||
# - chart-example.local
|
||||
|
||||
resources: {}
|
||||
# We usually recommend not to specify default resources and to leave this as a conscious
|
||||
# choice for the user. This also increases chances charts run on environments with little
|
||||
# resources, such as Minikube. If you do want to specify resources, uncomment the following
|
||||
# lines, adjust them as necessary, and remove the curly braces after 'resources:'.
|
||||
# limits:
|
||||
# cpu: 100m
|
||||
# memory: 128Mi
|
||||
# requests:
|
||||
# cpu: 100m
|
||||
# memory: 128Mi
|
||||
|
||||
autoscaling:
|
||||
enabled: false
|
||||
minReplicas: 1
|
||||
maxReplicas: 100
|
||||
targetCPUUtilizationPercentage: 80
|
||||
# targetMemoryUtilizationPercentage: 80
|
||||
|
||||
nodeSelector: {}
|
||||
|
||||
tolerations: []
|
||||
|
||||
affinity: {}
|
@ -0,0 +1,240 @@
|
||||
/*
|
||||
Copyright The Helm Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package registry // import "helm.sh/helm/v3/pkg/registry"
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
|
||||
"helm.sh/helm/v3/pkg/chart"
|
||||
helmtime "helm.sh/helm/v3/pkg/time"
|
||||
)
|
||||
|
||||
func TestGenerateOCIChartAnnotations(t *testing.T) {
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
chart *chart.Metadata
|
||||
expect map[string]string
|
||||
}{
|
||||
{
|
||||
"Baseline chart",
|
||||
&chart.Metadata{
|
||||
Name: "oci",
|
||||
Version: "0.0.1",
|
||||
},
|
||||
map[string]string{
|
||||
"org.opencontainers.image.title": "oci",
|
||||
"org.opencontainers.image.version": "0.0.1",
|
||||
},
|
||||
},
|
||||
{
|
||||
"Simple chart values",
|
||||
&chart.Metadata{
|
||||
Name: "oci",
|
||||
Version: "0.0.1",
|
||||
Description: "OCI Helm Chart",
|
||||
Home: "https://helm.sh",
|
||||
},
|
||||
map[string]string{
|
||||
"org.opencontainers.image.title": "oci",
|
||||
"org.opencontainers.image.version": "0.0.1",
|
||||
"org.opencontainers.image.description": "OCI Helm Chart",
|
||||
"org.opencontainers.image.url": "https://helm.sh",
|
||||
},
|
||||
},
|
||||
{
|
||||
"Maintainer without email",
|
||||
&chart.Metadata{
|
||||
Name: "oci",
|
||||
Version: "0.0.1",
|
||||
Description: "OCI Helm Chart",
|
||||
Home: "https://helm.sh",
|
||||
Maintainers: []*chart.Maintainer{
|
||||
{
|
||||
Name: "John Snow",
|
||||
},
|
||||
},
|
||||
},
|
||||
map[string]string{
|
||||
"org.opencontainers.image.title": "oci",
|
||||
"org.opencontainers.image.version": "0.0.1",
|
||||
"org.opencontainers.image.description": "OCI Helm Chart",
|
||||
"org.opencontainers.image.url": "https://helm.sh",
|
||||
"org.opencontainers.image.authors": "John Snow",
|
||||
},
|
||||
},
|
||||
{
|
||||
"Maintainer with email",
|
||||
&chart.Metadata{
|
||||
Name: "oci",
|
||||
Version: "0.0.1",
|
||||
Description: "OCI Helm Chart",
|
||||
Home: "https://helm.sh",
|
||||
Maintainers: []*chart.Maintainer{
|
||||
{Name: "John Snow", Email: "john@winterfell.com"},
|
||||
},
|
||||
},
|
||||
map[string]string{
|
||||
"org.opencontainers.image.title": "oci",
|
||||
"org.opencontainers.image.version": "0.0.1",
|
||||
"org.opencontainers.image.description": "OCI Helm Chart",
|
||||
"org.opencontainers.image.url": "https://helm.sh",
|
||||
"org.opencontainers.image.authors": "John Snow (john@winterfell.com)",
|
||||
},
|
||||
},
|
||||
{
|
||||
"Multiple Maintainers",
|
||||
&chart.Metadata{
|
||||
Name: "oci",
|
||||
Version: "0.0.1",
|
||||
Description: "OCI Helm Chart",
|
||||
Home: "https://helm.sh",
|
||||
Maintainers: []*chart.Maintainer{
|
||||
{Name: "John Snow", Email: "john@winterfell.com"},
|
||||
{Name: "Jane Snow"},
|
||||
},
|
||||
},
|
||||
map[string]string{
|
||||
"org.opencontainers.image.title": "oci",
|
||||
"org.opencontainers.image.version": "0.0.1",
|
||||
"org.opencontainers.image.description": "OCI Helm Chart",
|
||||
"org.opencontainers.image.url": "https://helm.sh",
|
||||
"org.opencontainers.image.authors": "John Snow (john@winterfell.com), Jane Snow",
|
||||
},
|
||||
},
|
||||
{
|
||||
"Chart with Sources",
|
||||
&chart.Metadata{
|
||||
Name: "oci",
|
||||
Version: "0.0.1",
|
||||
Description: "OCI Helm Chart",
|
||||
Sources: []string{
|
||||
"https://github.com/helm/helm",
|
||||
},
|
||||
},
|
||||
map[string]string{
|
||||
"org.opencontainers.image.title": "oci",
|
||||
"org.opencontainers.image.version": "0.0.1",
|
||||
"org.opencontainers.image.description": "OCI Helm Chart",
|
||||
"org.opencontainers.image.source": "https://github.com/helm/helm",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
|
||||
result := generateChartOCIAnnotations(tt.chart, true)
|
||||
|
||||
if !reflect.DeepEqual(tt.expect, result) {
|
||||
t.Errorf("%s: expected map %v, got %v", tt.name, tt.expect, result)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerateOCIAnnotations(t *testing.T) {
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
chart *chart.Metadata
|
||||
expect map[string]string
|
||||
}{
|
||||
{
|
||||
"Baseline chart",
|
||||
&chart.Metadata{
|
||||
Name: "oci",
|
||||
Version: "0.0.1",
|
||||
},
|
||||
map[string]string{
|
||||
"org.opencontainers.image.title": "oci",
|
||||
"org.opencontainers.image.version": "0.0.1",
|
||||
},
|
||||
},
|
||||
{
|
||||
"Simple chart values with custom Annotations",
|
||||
&chart.Metadata{
|
||||
Name: "oci",
|
||||
Version: "0.0.1",
|
||||
Description: "OCI Helm Chart",
|
||||
Annotations: map[string]string{
|
||||
"extrakey": "extravlue",
|
||||
"anotherkey": "anothervalue",
|
||||
},
|
||||
},
|
||||
map[string]string{
|
||||
"org.opencontainers.image.title": "oci",
|
||||
"org.opencontainers.image.version": "0.0.1",
|
||||
"org.opencontainers.image.description": "OCI Helm Chart",
|
||||
"extrakey": "extravlue",
|
||||
"anotherkey": "anothervalue",
|
||||
},
|
||||
},
|
||||
{
|
||||
"Verify Chart Name and Version cannot be overridden from annotations",
|
||||
&chart.Metadata{
|
||||
Name: "oci",
|
||||
Version: "0.0.1",
|
||||
Description: "OCI Helm Chart",
|
||||
Annotations: map[string]string{
|
||||
"org.opencontainers.image.title": "badchartname",
|
||||
"org.opencontainers.image.version": "1.0.0",
|
||||
"extrakey": "extravlue",
|
||||
},
|
||||
},
|
||||
map[string]string{
|
||||
"org.opencontainers.image.title": "oci",
|
||||
"org.opencontainers.image.version": "0.0.1",
|
||||
"org.opencontainers.image.description": "OCI Helm Chart",
|
||||
"extrakey": "extravlue",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
|
||||
result := generateOCIAnnotations(tt.chart, true)
|
||||
|
||||
if !reflect.DeepEqual(tt.expect, result) {
|
||||
t.Errorf("%s: expected map %v, got %v", tt.name, tt.expect, result)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerateOCICreatedAnnotations(t *testing.T) {
|
||||
chart := &chart.Metadata{
|
||||
Name: "oci",
|
||||
Version: "0.0.1",
|
||||
}
|
||||
|
||||
result := generateOCIAnnotations(chart, false)
|
||||
|
||||
// Check that created annotation exists
|
||||
if _, ok := result[ocispec.AnnotationCreated]; !ok {
|
||||
t.Errorf("%s annotation not created", ocispec.AnnotationCreated)
|
||||
}
|
||||
|
||||
// Verify value of created artifact in RFC3339 format
|
||||
if _, err := helmtime.Parse(time.RFC3339, result[ocispec.AnnotationCreated]); err != nil {
|
||||
t.Errorf("%s annotation with value '%s' not in RFC3339 format", ocispec.AnnotationCreated, result[ocispec.AnnotationCreated])
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,244 @@
|
||||
/*
|
||||
Copyright The Helm Authors.
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package strvals
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"strconv"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// ParseLiteral parses a set line interpreting the value as a literal string.
|
||||
//
|
||||
// A set line is of the form name1=value1
|
||||
func ParseLiteral(s string) (map[string]interface{}, error) {
|
||||
vals := map[string]interface{}{}
|
||||
scanner := bytes.NewBufferString(s)
|
||||
t := newLiteralParser(scanner, vals)
|
||||
err := t.parse()
|
||||
return vals, err
|
||||
}
|
||||
|
||||
// ParseLiteralInto parses a strvals line and merges the result into dest.
|
||||
// The value is interpreted as a literal string.
|
||||
//
|
||||
// If the strval string has a key that exists in dest, it overwrites the
|
||||
// dest version.
|
||||
func ParseLiteralInto(s string, dest map[string]interface{}) error {
|
||||
scanner := bytes.NewBufferString(s)
|
||||
t := newLiteralParser(scanner, dest)
|
||||
return t.parse()
|
||||
}
|
||||
|
||||
// literalParser is a simple parser that takes a strvals line and parses
|
||||
// it into a map representation.
|
||||
//
|
||||
// Values are interpreted as a literal string.
|
||||
//
|
||||
// where sc is the source of the original data being parsed
|
||||
// where data is the final parsed data from the parses with correct types
|
||||
type literalParser struct {
|
||||
sc *bytes.Buffer
|
||||
data map[string]interface{}
|
||||
}
|
||||
|
||||
func newLiteralParser(sc *bytes.Buffer, data map[string]interface{}) *literalParser {
|
||||
return &literalParser{sc: sc, data: data}
|
||||
}
|
||||
|
||||
func (t *literalParser) parse() error {
|
||||
for {
|
||||
err := t.key(t.data, 0)
|
||||
if err == nil {
|
||||
continue
|
||||
}
|
||||
if err == io.EOF {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
func runesUntilLiteral(in io.RuneReader, stop map[rune]bool) ([]rune, rune, error) {
|
||||
v := []rune{}
|
||||
for {
|
||||
switch r, _, e := in.ReadRune(); {
|
||||
case e != nil:
|
||||
return v, r, e
|
||||
case inMap(r, stop):
|
||||
return v, r, nil
|
||||
default:
|
||||
v = append(v, r)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (t *literalParser) key(data map[string]interface{}, nestedNameLevel int) (reterr error) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
reterr = fmt.Errorf("unable to parse key: %s", r)
|
||||
}
|
||||
}()
|
||||
stop := runeSet([]rune{'=', '[', '.'})
|
||||
for {
|
||||
switch key, lastRune, err := runesUntilLiteral(t.sc, stop); {
|
||||
case err != nil:
|
||||
if len(key) == 0 {
|
||||
return err
|
||||
}
|
||||
return errors.Errorf("key %q has no value", string(key))
|
||||
|
||||
case lastRune == '=':
|
||||
// found end of key: swallow the '=' and get the value
|
||||
value, err := t.val()
|
||||
if err == nil && err != io.EOF {
|
||||
return err
|
||||
}
|
||||
set(data, string(key), string(value))
|
||||
return nil
|
||||
|
||||
case lastRune == '.':
|
||||
// Check value name is within the maximum nested name level
|
||||
nestedNameLevel++
|
||||
if nestedNameLevel > MaxNestedNameLevel {
|
||||
return fmt.Errorf("value name nested level is greater than maximum supported nested level of %d", MaxNestedNameLevel)
|
||||
}
|
||||
|
||||
// first, create or find the target map in the given data
|
||||
inner := map[string]interface{}{}
|
||||
if _, ok := data[string(key)]; ok {
|
||||
inner = data[string(key)].(map[string]interface{})
|
||||
}
|
||||
|
||||
// recurse on sub-tree with remaining data
|
||||
err := t.key(inner, nestedNameLevel)
|
||||
if err == nil && len(inner) == 0 {
|
||||
return errors.Errorf("key map %q has no value", string(key))
|
||||
}
|
||||
if len(inner) != 0 {
|
||||
set(data, string(key), inner)
|
||||
}
|
||||
return err
|
||||
|
||||
case lastRune == '[':
|
||||
// We are in a list index context, so we need to set an index.
|
||||
i, err := t.keyIndex()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error parsing index")
|
||||
}
|
||||
kk := string(key)
|
||||
|
||||
// find or create target list
|
||||
list := []interface{}{}
|
||||
if _, ok := data[kk]; ok {
|
||||
list = data[kk].([]interface{})
|
||||
}
|
||||
|
||||
// now we need to get the value after the ]
|
||||
list, err = t.listItem(list, i, nestedNameLevel)
|
||||
set(data, kk, list)
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (t *literalParser) keyIndex() (int, error) {
|
||||
// First, get the key.
|
||||
stop := runeSet([]rune{']'})
|
||||
v, _, err := runesUntilLiteral(t.sc, stop)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// v should be the index
|
||||
return strconv.Atoi(string(v))
|
||||
}
|
||||
|
||||
func (t *literalParser) listItem(list []interface{}, i, nestedNameLevel int) ([]interface{}, error) {
|
||||
if i < 0 {
|
||||
return list, fmt.Errorf("negative %d index not allowed", i)
|
||||
}
|
||||
stop := runeSet([]rune{'[', '.', '='})
|
||||
|
||||
switch key, lastRune, err := runesUntilLiteral(t.sc, stop); {
|
||||
case len(key) > 0:
|
||||
return list, errors.Errorf("unexpected data at end of array index: %q", key)
|
||||
|
||||
case err != nil:
|
||||
return list, err
|
||||
|
||||
case lastRune == '=':
|
||||
value, err := t.val()
|
||||
if err != nil && err != io.EOF {
|
||||
return list, err
|
||||
}
|
||||
return setIndex(list, i, string(value))
|
||||
|
||||
case lastRune == '.':
|
||||
// we have a nested object. Send to t.key
|
||||
inner := map[string]interface{}{}
|
||||
if len(list) > i {
|
||||
var ok bool
|
||||
inner, ok = list[i].(map[string]interface{})
|
||||
if !ok {
|
||||
// We have indices out of order. Initialize empty value.
|
||||
list[i] = map[string]interface{}{}
|
||||
inner = list[i].(map[string]interface{})
|
||||
}
|
||||
}
|
||||
|
||||
// recurse
|
||||
err := t.key(inner, nestedNameLevel)
|
||||
if err != nil {
|
||||
return list, err
|
||||
}
|
||||
return setIndex(list, i, inner)
|
||||
|
||||
case lastRune == '[':
|
||||
// now we have a nested list. Read the index and handle.
|
||||
nextI, err := t.keyIndex()
|
||||
if err != nil {
|
||||
return list, errors.Wrap(err, "error parsing index")
|
||||
}
|
||||
var crtList []interface{}
|
||||
if len(list) > i {
|
||||
// If nested list already exists, take the value of list to next cycle.
|
||||
existed := list[i]
|
||||
if existed != nil {
|
||||
crtList = list[i].([]interface{})
|
||||
}
|
||||
}
|
||||
|
||||
// Now we need to get the value after the ].
|
||||
list2, err := t.listItem(crtList, nextI, nestedNameLevel)
|
||||
if err != nil {
|
||||
return list, err
|
||||
}
|
||||
return setIndex(list, i, list2)
|
||||
|
||||
default:
|
||||
return nil, errors.Errorf("parse error: unexpected token %v", lastRune)
|
||||
}
|
||||
}
|
||||
|
||||
func (t *literalParser) val() ([]rune, error) {
|
||||
stop := runeSet([]rune{})
|
||||
v, _, err := runesUntilLiteral(t.sc, stop)
|
||||
return v, err
|
||||
}
|
@ -0,0 +1,480 @@
|
||||
/*
|
||||
Copyright The Helm Authors.
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package strvals
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"sigs.k8s.io/yaml"
|
||||
)
|
||||
|
||||
func TestParseLiteral(t *testing.T) {
|
||||
cases := []struct {
|
||||
str string
|
||||
expect map[string]interface{}
|
||||
err bool
|
||||
}{
|
||||
{
|
||||
str: "name",
|
||||
err: true,
|
||||
},
|
||||
{
|
||||
str: "name=",
|
||||
expect: map[string]interface{}{"name": ""},
|
||||
},
|
||||
{
|
||||
str: "name=value",
|
||||
expect: map[string]interface{}{"name": "value"},
|
||||
err: false,
|
||||
},
|
||||
{
|
||||
str: "long_int_string=1234567890",
|
||||
expect: map[string]interface{}{"long_int_string": "1234567890"},
|
||||
err: false,
|
||||
},
|
||||
{
|
||||
str: "boolean=true",
|
||||
expect: map[string]interface{}{"boolean": "true"},
|
||||
err: false,
|
||||
},
|
||||
{
|
||||
str: "is_null=null",
|
||||
expect: map[string]interface{}{"is_null": "null"},
|
||||
err: false,
|
||||
},
|
||||
{
|
||||
str: "zero=0",
|
||||
expect: map[string]interface{}{"zero": "0"},
|
||||
err: false,
|
||||
},
|
||||
{
|
||||
str: "name1=null,name2=value2",
|
||||
expect: map[string]interface{}{"name1": "null,name2=value2"},
|
||||
err: false,
|
||||
},
|
||||
{
|
||||
str: "name1=value,,,tail",
|
||||
expect: map[string]interface{}{"name1": "value,,,tail"},
|
||||
err: false,
|
||||
},
|
||||
{
|
||||
str: "leading_zeros=00009",
|
||||
expect: map[string]interface{}{"leading_zeros": "00009"},
|
||||
err: false,
|
||||
},
|
||||
{
|
||||
str: "name=one two three",
|
||||
expect: map[string]interface{}{"name": "one two three"},
|
||||
err: false,
|
||||
},
|
||||
{
|
||||
str: "outer.inner=value",
|
||||
expect: map[string]interface{}{"outer": map[string]interface{}{"inner": "value"}},
|
||||
err: false,
|
||||
},
|
||||
{
|
||||
str: "outer.middle.inner=value",
|
||||
expect: map[string]interface{}{"outer": map[string]interface{}{"middle": map[string]interface{}{"inner": "value"}}},
|
||||
err: false,
|
||||
},
|
||||
{
|
||||
str: "name1.name2",
|
||||
err: true,
|
||||
},
|
||||
{
|
||||
str: "name1.name2=",
|
||||
expect: map[string]interface{}{"name1": map[string]interface{}{"name2": ""}},
|
||||
err: false,
|
||||
},
|
||||
{
|
||||
str: "name1.=name2",
|
||||
err: true,
|
||||
},
|
||||
{
|
||||
str: "name1.,name2",
|
||||
err: true,
|
||||
},
|
||||
{
|
||||
str: "name1={value1,value2}",
|
||||
expect: map[string]interface{}{"name1": "{value1,value2}"},
|
||||
},
|
||||
|
||||
// List support
|
||||
{
|
||||
str: "list[0]=foo",
|
||||
expect: map[string]interface{}{"list": []string{"foo"}},
|
||||
err: false,
|
||||
},
|
||||
{
|
||||
str: "list[0].foo=bar",
|
||||
expect: map[string]interface{}{
|
||||
"list": []interface{}{
|
||||
map[string]interface{}{"foo": "bar"},
|
||||
},
|
||||
},
|
||||
err: false,
|
||||
},
|
||||
{
|
||||
str: "list[-30].hello=world",
|
||||
err: true,
|
||||
},
|
||||
{
|
||||
str: "list[3]=bar",
|
||||
expect: map[string]interface{}{"list": []interface{}{nil, nil, nil, "bar"}},
|
||||
err: false,
|
||||
},
|
||||
{
|
||||
str: "illegal[0]name.foo=bar",
|
||||
err: true,
|
||||
},
|
||||
{
|
||||
str: "noval[0]",
|
||||
expect: map[string]interface{}{"noval": []interface{}{}},
|
||||
err: false,
|
||||
},
|
||||
{
|
||||
str: "noval[0]=",
|
||||
expect: map[string]interface{}{"noval": []interface{}{""}},
|
||||
err: false,
|
||||
},
|
||||
{
|
||||
str: "nested[0][0]=1",
|
||||
expect: map[string]interface{}{"nested": []interface{}{[]interface{}{"1"}}},
|
||||
err: false,
|
||||
},
|
||||
{
|
||||
str: "nested[1][1]=1",
|
||||
expect: map[string]interface{}{"nested": []interface{}{nil, []interface{}{nil, "1"}}},
|
||||
err: false,
|
||||
},
|
||||
{
|
||||
str: "name1.name2[0].foo=bar",
|
||||
expect: map[string]interface{}{
|
||||
"name1": map[string]interface{}{
|
||||
"name2": []map[string]interface{}{{"foo": "bar"}},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
str: "name1.name2[1].foo=bar",
|
||||
expect: map[string]interface{}{
|
||||
"name1": map[string]interface{}{
|
||||
"name2": []map[string]interface{}{nil, {"foo": "bar"}},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
str: "name1.name2[1].foo=bar",
|
||||
expect: map[string]interface{}{
|
||||
"name1": map[string]interface{}{
|
||||
"name2": []map[string]interface{}{nil, {"foo": "bar"}},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
str: "]={}].",
|
||||
expect: map[string]interface{}{"]": "{}]."},
|
||||
err: false,
|
||||
},
|
||||
|
||||
// issue test cases: , = $ ( ) { } . \ \\
|
||||
{
|
||||
str: "name=val,val",
|
||||
expect: map[string]interface{}{"name": "val,val"},
|
||||
err: false,
|
||||
},
|
||||
{
|
||||
str: "name=val.val",
|
||||
expect: map[string]interface{}{"name": "val.val"},
|
||||
err: false,
|
||||
},
|
||||
{
|
||||
str: "name=val=val",
|
||||
expect: map[string]interface{}{"name": "val=val"},
|
||||
err: false,
|
||||
},
|
||||
{
|
||||
str: "name=val$val",
|
||||
expect: map[string]interface{}{"name": "val$val"},
|
||||
err: false,
|
||||
},
|
||||
{
|
||||
str: "name=(value",
|
||||
expect: map[string]interface{}{"name": "(value"},
|
||||
err: false,
|
||||
},
|
||||
{
|
||||
str: "name=value)",
|
||||
expect: map[string]interface{}{"name": "value)"},
|
||||
err: false,
|
||||
},
|
||||
{
|
||||
str: "name=(value)",
|
||||
expect: map[string]interface{}{"name": "(value)"},
|
||||
err: false,
|
||||
},
|
||||
{
|
||||
str: "name={value",
|
||||
expect: map[string]interface{}{"name": "{value"},
|
||||
err: false,
|
||||
},
|
||||
{
|
||||
str: "name=value}",
|
||||
expect: map[string]interface{}{"name": "value}"},
|
||||
err: false,
|
||||
},
|
||||
{
|
||||
str: "name={value}",
|
||||
expect: map[string]interface{}{"name": "{value}"},
|
||||
err: false,
|
||||
},
|
||||
{
|
||||
str: "name={value1,value2}",
|
||||
expect: map[string]interface{}{"name": "{value1,value2}"},
|
||||
err: false,
|
||||
},
|
||||
{
|
||||
str: `name=val\val`,
|
||||
expect: map[string]interface{}{"name": `val\val`},
|
||||
err: false,
|
||||
},
|
||||
{
|
||||
str: `name=val\\val`,
|
||||
expect: map[string]interface{}{"name": `val\\val`},
|
||||
err: false,
|
||||
},
|
||||
{
|
||||
str: `name=val\\\val`,
|
||||
expect: map[string]interface{}{"name": `val\\\val`},
|
||||
err: false,
|
||||
},
|
||||
{
|
||||
str: `name={val,.?*v\0a!l)some`,
|
||||
expect: map[string]interface{}{"name": `{val,.?*v\0a!l)some`},
|
||||
err: false,
|
||||
},
|
||||
{
|
||||
str: `name=em%GT)tqUDqz,i-\h+Mbqs-!:.m\\rE=mkbM#rR}@{-k@`,
|
||||
expect: map[string]interface{}{"name": `em%GT)tqUDqz,i-\h+Mbqs-!:.m\\rE=mkbM#rR}@{-k@`},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range cases {
|
||||
got, err := ParseLiteral(tt.str)
|
||||
if err != nil {
|
||||
if !tt.err {
|
||||
t.Fatalf("%s: %s", tt.str, err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if tt.err {
|
||||
t.Errorf("%s: Expected error. Got nil", tt.str)
|
||||
}
|
||||
|
||||
y1, err := yaml.Marshal(tt.expect)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
y2, err := yaml.Marshal(got)
|
||||
if err != nil {
|
||||
t.Fatalf("Error serializing parsed value: %s", err)
|
||||
}
|
||||
|
||||
if string(y1) != string(y2) {
|
||||
t.Errorf("%s: Expected:\n%s\nGot:\n%s", tt.str, y1, y2)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseLiteralInto(t *testing.T) {
|
||||
tests := []struct {
|
||||
input string
|
||||
input2 string
|
||||
got map[string]interface{}
|
||||
expect map[string]interface{}
|
||||
err bool
|
||||
}{
|
||||
{
|
||||
input: "outer.inner1=value1,outer.inner3=value3,outer.inner4=4",
|
||||
got: map[string]interface{}{
|
||||
"outer": map[string]interface{}{
|
||||
"inner1": "overwrite",
|
||||
"inner2": "value2",
|
||||
},
|
||||
},
|
||||
expect: map[string]interface{}{
|
||||
"outer": map[string]interface{}{
|
||||
"inner1": "value1,outer.inner3=value3,outer.inner4=4",
|
||||
"inner2": "value2",
|
||||
}},
|
||||
err: false,
|
||||
},
|
||||
{
|
||||
input: "listOuter[0][0].type=listValue",
|
||||
input2: "listOuter[0][0].status=alive",
|
||||
got: map[string]interface{}{},
|
||||
expect: map[string]interface{}{
|
||||
"listOuter": [][]interface{}{{map[string]string{
|
||||
"type": "listValue",
|
||||
"status": "alive",
|
||||
}}},
|
||||
},
|
||||
err: false,
|
||||
},
|
||||
{
|
||||
input: "listOuter[0][0].type=listValue",
|
||||
input2: "listOuter[1][0].status=alive",
|
||||
got: map[string]interface{}{},
|
||||
expect: map[string]interface{}{
|
||||
"listOuter": [][]interface{}{
|
||||
{
|
||||
map[string]string{"type": "listValue"},
|
||||
},
|
||||
{
|
||||
map[string]string{"status": "alive"},
|
||||
},
|
||||
},
|
||||
},
|
||||
err: false,
|
||||
},
|
||||
{
|
||||
input: "listOuter[0][1][0].type=listValue",
|
||||
input2: "listOuter[0][0][1].status=alive",
|
||||
got: map[string]interface{}{
|
||||
"listOuter": []interface{}{
|
||||
[]interface{}{
|
||||
[]interface{}{
|
||||
map[string]string{"exited": "old"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expect: map[string]interface{}{
|
||||
"listOuter": [][][]interface{}{
|
||||
{
|
||||
{
|
||||
map[string]string{"exited": "old"},
|
||||
map[string]string{"status": "alive"},
|
||||
},
|
||||
{
|
||||
map[string]string{"type": "listValue"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
err: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
if err := ParseLiteralInto(tt.input, tt.got); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if tt.err {
|
||||
t.Errorf("%s: Expected error. Got nil", tt.input)
|
||||
}
|
||||
|
||||
if tt.input2 != "" {
|
||||
if err := ParseLiteralInto(tt.input2, tt.got); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if tt.err {
|
||||
t.Errorf("%s: Expected error. Got nil", tt.input2)
|
||||
}
|
||||
}
|
||||
|
||||
y1, err := yaml.Marshal(tt.expect)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
y2, err := yaml.Marshal(tt.got)
|
||||
if err != nil {
|
||||
t.Fatalf("Error serializing parsed value: %s", err)
|
||||
}
|
||||
|
||||
if string(y1) != string(y2) {
|
||||
t.Errorf("%s: Expected:\n%s\nGot:\n%s", tt.input, y1, y2)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseLiteralNestedLevels(t *testing.T) {
|
||||
var keyMultipleNestedLevels string
|
||||
|
||||
for i := 1; i <= MaxNestedNameLevel+2; i++ {
|
||||
tmpStr := fmt.Sprintf("name%d", i)
|
||||
if i <= MaxNestedNameLevel+1 {
|
||||
tmpStr = tmpStr + "."
|
||||
}
|
||||
keyMultipleNestedLevels += tmpStr
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
str string
|
||||
expect map[string]interface{}
|
||||
err bool
|
||||
errStr string
|
||||
}{
|
||||
{
|
||||
"outer.middle.inner=value",
|
||||
map[string]interface{}{"outer": map[string]interface{}{"middle": map[string]interface{}{"inner": "value"}}},
|
||||
false,
|
||||
"",
|
||||
},
|
||||
{
|
||||
str: keyMultipleNestedLevels + "=value",
|
||||
err: true,
|
||||
errStr: fmt.Sprintf("value name nested level is greater than maximum supported nested level of %d", MaxNestedNameLevel),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
got, err := ParseLiteral(tt.str)
|
||||
if err != nil {
|
||||
if tt.err {
|
||||
if tt.errStr != "" {
|
||||
if err.Error() != tt.errStr {
|
||||
t.Errorf("Expected error: %s. Got error: %s", tt.errStr, err.Error())
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
t.Fatalf("%s: %s", tt.str, err)
|
||||
}
|
||||
|
||||
if tt.err {
|
||||
t.Errorf("%s: Expected error. Got nil", tt.str)
|
||||
}
|
||||
|
||||
y1, err := yaml.Marshal(tt.expect)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
y2, err := yaml.Marshal(got)
|
||||
if err != nil {
|
||||
t.Fatalf("Error serializing parsed value: %s", err)
|
||||
}
|
||||
|
||||
if string(y1) != string(y2) {
|
||||
t.Errorf("%s: Expected:\n%s\nGot:\n%s", tt.str, y1, y2)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in new issue