devfile 0.0.4.pre.alpha1-arm64-darwin
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/bin/devfile +0 -0
- data/ext/devfile.go +205 -0
- data/ext/go.mod +119 -0
- data/ext/go.sum +1568 -0
- data/ext/main.go +203 -0
- data/ext/volume.go +114 -0
- data/lib/devfile.rb +55 -0
- metadata +50 -0
data/ext/main.go
ADDED
@@ -0,0 +1,203 @@
|
|
1
|
+
package main
|
2
|
+
|
3
|
+
import (
|
4
|
+
"fmt"
|
5
|
+
"k8s.io/apimachinery/pkg/util/yaml"
|
6
|
+
"os"
|
7
|
+
"strconv"
|
8
|
+
|
9
|
+
"k8s.io/apimachinery/pkg/runtime"
|
10
|
+
)
|
11
|
+
|
12
|
+
type Result struct {
|
13
|
+
content string
|
14
|
+
err error
|
15
|
+
}
|
16
|
+
|
17
|
+
func main() {
|
18
|
+
args := os.Args
|
19
|
+
|
20
|
+
if len(args) <= 1 {
|
21
|
+
fmt.Fprint(os.Stderr, "Function name and devfile are required")
|
22
|
+
os.Exit(1)
|
23
|
+
}
|
24
|
+
|
25
|
+
fnName := os.Args[1]
|
26
|
+
devfile := os.Args[2]
|
27
|
+
|
28
|
+
var result Result
|
29
|
+
|
30
|
+
switch fnName {
|
31
|
+
case "deployment":
|
32
|
+
result = getDeployment(devfile, args[3], args[4], args[5], args[6], args[7])
|
33
|
+
case "service":
|
34
|
+
result = getService(devfile, args[3], args[4], args[5], args[6])
|
35
|
+
case "ingress":
|
36
|
+
result = getIngress(devfile, args[3], args[4], args[5], args[6], args[7], args[8])
|
37
|
+
case "all":
|
38
|
+
result = getAll(devfile, args[3], args[4], args[5], args[6], args[7], args[8], args[9])
|
39
|
+
case "flatten":
|
40
|
+
result = flatten(devfile)
|
41
|
+
}
|
42
|
+
|
43
|
+
if result.err != nil {
|
44
|
+
fmt.Fprint(os.Stderr, result.err)
|
45
|
+
os.Exit(1)
|
46
|
+
}
|
47
|
+
|
48
|
+
fmt.Print(result.content)
|
49
|
+
}
|
50
|
+
|
51
|
+
func unmarshalKeyValuePair(data string) (map[string]string, error) {
|
52
|
+
values := map[string]string{}
|
53
|
+
err := yaml.Unmarshal([]byte(data), &values)
|
54
|
+
if err != nil {
|
55
|
+
return nil, err
|
56
|
+
}
|
57
|
+
return values, err
|
58
|
+
}
|
59
|
+
|
60
|
+
func getDeployment(devfile, name, namespace, labelsStr, annotationsStr, replicas string) Result {
|
61
|
+
d, err := parseDevfile(devfile)
|
62
|
+
if err != nil {
|
63
|
+
return Result{"", err}
|
64
|
+
}
|
65
|
+
exists, err := d.hasContainerComponents()
|
66
|
+
if err != nil {
|
67
|
+
return Result{"", err}
|
68
|
+
}
|
69
|
+
if exists == false {
|
70
|
+
return Result{"", err}
|
71
|
+
}
|
72
|
+
labels, err := unmarshalKeyValuePair(labelsStr)
|
73
|
+
if err != nil {
|
74
|
+
return Result{"", err}
|
75
|
+
}
|
76
|
+
annotations, err := unmarshalKeyValuePair(annotationsStr)
|
77
|
+
if err != nil {
|
78
|
+
return Result{"", err}
|
79
|
+
}
|
80
|
+
replicasInt, err := strconv.Atoi(replicas)
|
81
|
+
if err != nil {
|
82
|
+
return Result{"", err}
|
83
|
+
}
|
84
|
+
deployment, err := d.getDeployment(name, namespace, labels, annotations, replicasInt)
|
85
|
+
if err != nil {
|
86
|
+
return Result{"", err}
|
87
|
+
}
|
88
|
+
content, err := marshalResources([]runtime.Object{deployment})
|
89
|
+
if err != nil {
|
90
|
+
return Result{"", err}
|
91
|
+
}
|
92
|
+
return Result{content, nil}
|
93
|
+
}
|
94
|
+
|
95
|
+
func getService(devfile, name, namespace, labelsStr, annotationsStr string) Result {
|
96
|
+
d, err := parseDevfile(devfile)
|
97
|
+
if err != nil {
|
98
|
+
return Result{"", err}
|
99
|
+
}
|
100
|
+
exists, err := d.hasContainerComponents()
|
101
|
+
if err != nil {
|
102
|
+
return Result{"", err}
|
103
|
+
}
|
104
|
+
if exists == false {
|
105
|
+
return Result{"", err}
|
106
|
+
}
|
107
|
+
labels, err := unmarshalKeyValuePair(labelsStr)
|
108
|
+
if err != nil {
|
109
|
+
return Result{"", err}
|
110
|
+
}
|
111
|
+
annotations, err := unmarshalKeyValuePair(annotationsStr)
|
112
|
+
if err != nil {
|
113
|
+
return Result{"", err}
|
114
|
+
}
|
115
|
+
service, err := d.getService(name, namespace, labels, annotations)
|
116
|
+
if err != nil {
|
117
|
+
return Result{"", err}
|
118
|
+
}
|
119
|
+
content, err := marshalResources([]runtime.Object{service})
|
120
|
+
if err != nil {
|
121
|
+
return Result{"", err}
|
122
|
+
}
|
123
|
+
return Result{content, nil}
|
124
|
+
}
|
125
|
+
|
126
|
+
func getIngress(devfile, name, namespace, labelsStr, annotationsStr, domainSuffix, ingressClass string) Result {
|
127
|
+
d, err := parseDevfile(devfile)
|
128
|
+
if err != nil {
|
129
|
+
return Result{"", err}
|
130
|
+
}
|
131
|
+
exists, err := d.hasContainerComponents()
|
132
|
+
if err != nil {
|
133
|
+
return Result{"", err}
|
134
|
+
}
|
135
|
+
if exists == false {
|
136
|
+
return Result{"", err}
|
137
|
+
}
|
138
|
+
labels, err := unmarshalKeyValuePair(labelsStr)
|
139
|
+
if err != nil {
|
140
|
+
return Result{"", err}
|
141
|
+
}
|
142
|
+
annotations, err := unmarshalKeyValuePair(annotationsStr)
|
143
|
+
if err != nil {
|
144
|
+
return Result{"", err}
|
145
|
+
}
|
146
|
+
ingress, err := d.getIngress(name, namespace, labels, annotations, domainSuffix, ingressClass)
|
147
|
+
if err != nil {
|
148
|
+
return Result{"", err}
|
149
|
+
}
|
150
|
+
content, err := marshalResources([]runtime.Object{ingress})
|
151
|
+
if err != nil {
|
152
|
+
return Result{"", err}
|
153
|
+
}
|
154
|
+
return Result{content, nil}
|
155
|
+
}
|
156
|
+
|
157
|
+
func getAll(devfile string, name, namespace, labelsStr, annotationsStr, replicas, domainSuffix, ingressClass string) Result {
|
158
|
+
d, err := parseDevfile(devfile)
|
159
|
+
if err != nil {
|
160
|
+
return Result{"", err}
|
161
|
+
}
|
162
|
+
exists, err := d.hasContainerComponents()
|
163
|
+
if err != nil {
|
164
|
+
return Result{"", err}
|
165
|
+
}
|
166
|
+
if exists == false {
|
167
|
+
return Result{"", err}
|
168
|
+
}
|
169
|
+
labels, err := unmarshalKeyValuePair(labelsStr)
|
170
|
+
if err != nil {
|
171
|
+
return Result{"", err}
|
172
|
+
}
|
173
|
+
annotations, err := unmarshalKeyValuePair(annotationsStr)
|
174
|
+
if err != nil {
|
175
|
+
return Result{"", err}
|
176
|
+
}
|
177
|
+
replicasInt, err := strconv.Atoi(replicas)
|
178
|
+
if err != nil {
|
179
|
+
return Result{"", err}
|
180
|
+
}
|
181
|
+
resources, err := d.getAll(name, namespace, labels, annotations, replicasInt, domainSuffix, ingressClass)
|
182
|
+
if err != nil {
|
183
|
+
return Result{"", err}
|
184
|
+
}
|
185
|
+
content, err := marshalResources(resources)
|
186
|
+
if err != nil {
|
187
|
+
return Result{"", err}
|
188
|
+
}
|
189
|
+
return Result{content, nil}
|
190
|
+
}
|
191
|
+
|
192
|
+
func flatten(devfile string) Result {
|
193
|
+
d, err := parseDevfile(devfile)
|
194
|
+
if err != nil {
|
195
|
+
return Result{"", err}
|
196
|
+
}
|
197
|
+
flattenedDevfile := d.getFlattenedDevfileContent()
|
198
|
+
content, err := marshalDevfile(flattenedDevfile)
|
199
|
+
if err != nil {
|
200
|
+
return Result{"", err}
|
201
|
+
}
|
202
|
+
return Result{content, nil}
|
203
|
+
}
|
data/ext/volume.go
ADDED
@@ -0,0 +1,114 @@
|
|
1
|
+
package main
|
2
|
+
|
3
|
+
import (
|
4
|
+
"fmt"
|
5
|
+
"github.com/devfile/library/v2/pkg/devfile/generator"
|
6
|
+
"github.com/devfile/library/v2/pkg/devfile/parser/data/v2/common"
|
7
|
+
corev1 "k8s.io/api/core/v1"
|
8
|
+
"k8s.io/apimachinery/pkg/api/resource"
|
9
|
+
"strings"
|
10
|
+
)
|
11
|
+
|
12
|
+
func (d Devfile) getVolumesAndVolumeMounts(containers []corev1.Container, initContainers []corev1.Container, pvcNamePrefix string) ([]corev1.Volume, error) {
|
13
|
+
containerComponents, err := d.devfileObj.Data.GetDevfileContainerComponents(common.DevfileOptions{})
|
14
|
+
if err != nil {
|
15
|
+
return nil, err
|
16
|
+
}
|
17
|
+
volumeComponents, err := d.devfileObj.Data.GetDevfileVolumeComponents(common.DevfileOptions{})
|
18
|
+
if err != nil {
|
19
|
+
return nil, err
|
20
|
+
}
|
21
|
+
|
22
|
+
var volumes []corev1.Volume
|
23
|
+
for _, volumeComponent := range volumeComponents {
|
24
|
+
volName := volumeComponent.Name
|
25
|
+
if bool(*volumeComponent.Volume.Ephemeral) == true {
|
26
|
+
emptyDir, err := getEmptyDir(volName, volumeComponent.Volume.Size)
|
27
|
+
if err != nil {
|
28
|
+
return nil, err
|
29
|
+
}
|
30
|
+
volumes = append(volumes, emptyDir)
|
31
|
+
} else {
|
32
|
+
// TODO: figure this out; how should we pass PVC name? should the object be generated here?
|
33
|
+
pvcName := fmt.Sprintf("%s-%s", pvcNamePrefix, volName)
|
34
|
+
volumes = append(volumes, getPVC(volName, pvcName))
|
35
|
+
}
|
36
|
+
// containerNameToMountPaths is a map of the Devfile container name to their Devfile Volume Mount Paths for a given Volume Name
|
37
|
+
containerNameToMountPaths := make(map[string][]string)
|
38
|
+
for _, containerComp := range containerComponents {
|
39
|
+
for _, volumeMount := range containerComp.Container.VolumeMounts {
|
40
|
+
if volName == volumeMount.Name {
|
41
|
+
containerNameToMountPaths[containerComp.Name] = append(containerNameToMountPaths[containerComp.Name], generator.GetVolumeMountPath(volumeMount))
|
42
|
+
}
|
43
|
+
}
|
44
|
+
}
|
45
|
+
|
46
|
+
addVolumeMountToContainers(containers, initContainers, volName, containerNameToMountPaths)
|
47
|
+
}
|
48
|
+
|
49
|
+
return volumes, nil
|
50
|
+
}
|
51
|
+
|
52
|
+
// addVolumeMountToContainers adds the Volume Mounts in containerNameToMountPaths to the containers for a given pvc and volumeName
|
53
|
+
// containerNameToMountPaths is a map of a container name to an array of its Mount Paths.
|
54
|
+
// To be moved to devfile/library.
|
55
|
+
func addVolumeMountToContainers(containers []corev1.Container, initContainers []corev1.Container, volumeName string, containerNameToMountPaths map[string][]string) {
|
56
|
+
|
57
|
+
for containerName, mountPaths := range containerNameToMountPaths {
|
58
|
+
for i := range containers {
|
59
|
+
if containers[i].Name == containerName {
|
60
|
+
for _, mountPath := range mountPaths {
|
61
|
+
containers[i].VolumeMounts = append(containers[i].VolumeMounts, corev1.VolumeMount{
|
62
|
+
Name: volumeName,
|
63
|
+
MountPath: mountPath,
|
64
|
+
SubPath: "",
|
65
|
+
},
|
66
|
+
)
|
67
|
+
}
|
68
|
+
}
|
69
|
+
}
|
70
|
+
for i := range initContainers {
|
71
|
+
if strings.HasPrefix(initContainers[i].Name, containerName) {
|
72
|
+
for _, mountPath := range mountPaths {
|
73
|
+
initContainers[i].VolumeMounts = append(initContainers[i].VolumeMounts, corev1.VolumeMount{
|
74
|
+
Name: volumeName,
|
75
|
+
MountPath: mountPath,
|
76
|
+
SubPath: "",
|
77
|
+
},
|
78
|
+
)
|
79
|
+
}
|
80
|
+
}
|
81
|
+
}
|
82
|
+
}
|
83
|
+
}
|
84
|
+
|
85
|
+
// getPVC gets a pvc type volume with the given volume name and pvc name.
|
86
|
+
func getPVC(volumeName, pvcName string) corev1.Volume {
|
87
|
+
|
88
|
+
return corev1.Volume{
|
89
|
+
Name: volumeName,
|
90
|
+
VolumeSource: corev1.VolumeSource{
|
91
|
+
PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{
|
92
|
+
ClaimName: pvcName,
|
93
|
+
},
|
94
|
+
},
|
95
|
+
}
|
96
|
+
}
|
97
|
+
|
98
|
+
// getEmptyDir gets an emptyDir type volume with the given volume name and size.
|
99
|
+
// size should be parseable as a Kubernetes `Quantity` or an error will be returned
|
100
|
+
func getEmptyDir(volumeName string, size string) (corev1.Volume, error) {
|
101
|
+
|
102
|
+
emptyDir := &corev1.EmptyDirVolumeSource{}
|
103
|
+
qty, err := resource.ParseQuantity(size)
|
104
|
+
if err != nil {
|
105
|
+
return corev1.Volume{}, err
|
106
|
+
}
|
107
|
+
emptyDir.SizeLimit = &qty
|
108
|
+
return corev1.Volume{
|
109
|
+
Name: volumeName,
|
110
|
+
VolumeSource: corev1.VolumeSource{
|
111
|
+
EmptyDir: emptyDir,
|
112
|
+
},
|
113
|
+
}, nil
|
114
|
+
}
|
data/lib/devfile.rb
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'open3'
|
4
|
+
|
5
|
+
# Module that works with the Devfile standard
|
6
|
+
module Devfile
|
7
|
+
# Set of services to parse a devfile and output k8s manifests
|
8
|
+
class Parser
|
9
|
+
FILE_PATH = File.expand_path('./../bin/devfile', File.dirname(__FILE__))
|
10
|
+
|
11
|
+
class << self
|
12
|
+
def get_deployment(devfile, name, namespace, labels, annotations, replicas)
|
13
|
+
stdout, stderr, status = Open3.capture3("#{FILE_PATH} deployment '#{devfile}' #{name} #{namespace} '#{labels}' '#{annotations}' #{replicas}")
|
14
|
+
|
15
|
+
raise stderr unless status.success?
|
16
|
+
|
17
|
+
stdout
|
18
|
+
end
|
19
|
+
|
20
|
+
def get_service(devfile, name, namespace, labels, annotations)
|
21
|
+
stdout, stderr, status = Open3.capture3("#{FILE_PATH} service '#{devfile}' #{name} #{namespace} '#{labels}' '#{annotations}'")
|
22
|
+
|
23
|
+
raise stderr unless status.success?
|
24
|
+
|
25
|
+
stdout
|
26
|
+
end
|
27
|
+
|
28
|
+
def get_ingress(devfile, name, namespace, labels, annotations, domain_suffix, ingress_class)
|
29
|
+
stdout, stderr, status = Open3.capture3("#{FILE_PATH} ingress '#{devfile}' #{name} #{namespace} '#{labels}' '#{annotations}' #{domain_suffix} #{ingress_class}")
|
30
|
+
|
31
|
+
raise stderr unless status.success?
|
32
|
+
|
33
|
+
stdout
|
34
|
+
end
|
35
|
+
|
36
|
+
def get_all(devfile, name, namespace, labels, annotations, replicas, domain_suffix, ingress_class)
|
37
|
+
stdout, stderr, status = Open3.capture3(
|
38
|
+
"#{FILE_PATH} all '#{devfile}' #{name} #{namespace} '#{labels}' '#{annotations}' #{replicas} #{domain_suffix} #{ingress_class}"
|
39
|
+
)
|
40
|
+
|
41
|
+
raise stderr unless status.success?
|
42
|
+
|
43
|
+
stdout
|
44
|
+
end
|
45
|
+
|
46
|
+
def flatten(devfile)
|
47
|
+
stdout, stderr, status = Open3.capture3("#{FILE_PATH} flatten '#{devfile}'")
|
48
|
+
|
49
|
+
raise stderr unless status.success?
|
50
|
+
|
51
|
+
stdout
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
metadata
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: devfile
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.4.pre.alpha1
|
5
|
+
platform: arm64-darwin
|
6
|
+
authors:
|
7
|
+
- GitLab
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2023-03-08 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: Library used to generate kubernetes manifests from a Devfile.
|
14
|
+
email: spatnaik@gitlab.com
|
15
|
+
executables: []
|
16
|
+
extensions: []
|
17
|
+
extra_rdoc_files: []
|
18
|
+
files:
|
19
|
+
- bin/devfile
|
20
|
+
- ext/devfile.go
|
21
|
+
- ext/go.mod
|
22
|
+
- ext/go.sum
|
23
|
+
- ext/main.go
|
24
|
+
- ext/volume.go
|
25
|
+
- lib/devfile.rb
|
26
|
+
homepage: https://gitlab.com
|
27
|
+
licenses:
|
28
|
+
- MIT
|
29
|
+
metadata:
|
30
|
+
source_code_uri: https://gitlab.com/gitlab-org/remote-development/devfile-gem
|
31
|
+
post_install_message:
|
32
|
+
rdoc_options: []
|
33
|
+
require_paths:
|
34
|
+
- lib
|
35
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - ">="
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '2.7'
|
40
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
41
|
+
requirements:
|
42
|
+
- - ">"
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
version: 1.3.1
|
45
|
+
requirements: []
|
46
|
+
rubygems_version: 3.4.7
|
47
|
+
signing_key:
|
48
|
+
specification_version: 4
|
49
|
+
summary: Parse and generate kubernetes manifests from a Devfile
|
50
|
+
test_files: []
|