Skip to content

Commit 27309bb

Browse files
Try not to override too many existing things in input yaml (#2)
1 parent fa5e65c commit 27309bb

File tree

4 files changed

+229
-50
lines changed

4 files changed

+229
-50
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
.DS_Store
22
.releases
33
cloudsql-proxy-inject*
4+
5+
.idea

main.go

Lines changed: 108 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
package main
22

33
import (
4-
"encoding/json"
4+
"bytes"
55
"fmt"
6+
"github.com/pkg/errors"
7+
"io"
8+
"io/ioutil"
69
"os"
710

8-
"github.com/go-yaml/yaml"
9-
kingpin "gopkg.in/alecthomas/kingpin.v2"
11+
"gopkg.in/alecthomas/kingpin.v2"
1012
"k8s.io/api/apps/v1beta1"
11-
v1 "k8s.io/api/core/v1"
13+
"k8s.io/api/core/v1"
1214
"k8s.io/apimachinery/pkg/api/resource"
1315
k8sjson "k8s.io/apimachinery/pkg/runtime/serializer/json"
1416
k8syaml "k8s.io/apimachinery/pkg/util/yaml"
@@ -29,68 +31,41 @@ var (
2931

3032
func main() {
3133
kingpin.Parse()
34+
runInjector()
35+
}
3236

33-
var cloudSQLProxyContainer v1.Container
34-
{
35-
requestResources, limitResources := setResources(*cpuRequest, *memoryRequest, *cpuLimit, *memoryLimit)
36-
37-
var runAsUser int64 = 2
38-
var allowPrivilegeEscalation = false
39-
40-
securityContext := v1.SecurityContext{
41-
RunAsUser: &runAsUser,
42-
AllowPrivilegeEscalation: &allowPrivilegeEscalation,
43-
}
44-
45-
volumeMount := v1.VolumeMount{
46-
Name: "cloudsql-proxy-credentials",
47-
MountPath: "/secrets/cloudsql",
48-
ReadOnly: true,
49-
}
50-
51-
cloudSQLProxyContainer = v1.Container{}
52-
cloudSQLProxyContainer.Name = "cloudsql-proxy"
53-
cloudSQLProxyContainer.Image = fmt.Sprintf("gcr.io/cloudsql-docker/gce-proxy:%s", *proxyVersion)
54-
cloudSQLProxyContainer.Command = []string{"/cloud_sql_proxy", fmt.Sprintf("-instances=%s:%s:%s", *project, *region, *instance), "-log_debug_stdout=true", fmt.Sprintf("-verbose=%s", *verbose), "-credential_file=/secrets/cloudsql/credentials.json"}
55-
cloudSQLProxyContainer.Resources = v1.ResourceRequirements{Requests: requestResources, Limits: limitResources}
56-
cloudSQLProxyContainer.SecurityContext = &securityContext
57-
cloudSQLProxyContainer.VolumeMounts = []v1.VolumeMount{volumeMount}
58-
}
59-
60-
b, err := yaml.Marshal(&cloudSQLProxyContainer)
61-
if err != nil {
62-
panic(err)
63-
}
64-
f, err := os.Open(*path)
65-
if err != nil {
66-
panic(err)
67-
}
68-
defer f.Close()
37+
func runInjector() {
38+
cloudSQLProxyContainer := getCloudContainer()
6939

70-
b, err = json.Marshal(&cloudSQLProxyContainer)
71-
if err != nil {
72-
panic(err)
73-
}
40+
// split the file bytes by resources
41+
// a file may contains multiple resources, separated by "---"
42+
allK8SResources := getAllResourcesBytes(*path)
43+
// separate deployment from others resources
44+
deploymentBytes, otherResources := extractDeploymentBytes(allK8SResources)
7445

75-
deploy := &v1beta1.Deployment{}
76-
err = json.Unmarshal(b, &deploy)
46+
deploy := v1beta1.Deployment{}
47+
err := k8syaml.NewYAMLOrJSONDecoder(bytes.NewReader(deploymentBytes), 4096).Decode(&deploy)
7748
if err != nil {
7849
panic(err)
7950
}
80-
k8syaml.NewYAMLOrJSONDecoder(f, 4096).Decode(&deploy)
8151

82-
deploy.Spec.Template.Spec.Volumes = []v1.Volume{v1.Volume{
52+
deploy.Spec.Template.Spec.Volumes = append(deploy.Spec.Template.Spec.Volumes, v1.Volume{
8353
Name: "cloudsql-proxy-credentials",
8454
VolumeSource: v1.VolumeSource{
8555
Secret: &v1.SecretVolumeSource{
8656
SecretName: "cloudsql-proxy-credentials",
8757
},
8858
},
89-
}}
59+
})
9060
deploy.Spec.Template.Spec.Containers = append(deploy.Spec.Template.Spec.Containers, cloudSQLProxyContainer)
9161

9262
serializer := k8sjson.NewYAMLSerializer(k8sjson.DefaultMetaFactory, nil, nil)
93-
serializer.Encode(deploy, os.Stdout)
63+
64+
outputBytes := bytes.NewBuffer(nil)
65+
serializer.Encode(&deploy, outputBytes)
66+
putItBack(otherResources, outputBytes)
67+
68+
os.Stdout.Write(outputBytes.Bytes())
9469
}
9570

9671
func setResources(cpuRequest, memoryRequest, cpuLimit, memoryLimit string) (request v1.ResourceList, limit v1.ResourceList) {
@@ -123,3 +98,86 @@ func setResources(cpuRequest, memoryRequest, cpuLimit, memoryLimit string) (requ
12398

12499
return request, limit
125100
}
101+
102+
func getAllResourcesBytes(filepath string) [][]byte {
103+
f, err := os.Open(filepath)
104+
if err != nil {
105+
panic(err)
106+
}
107+
defer f.Close()
108+
109+
fileBytes, err := ioutil.ReadAll(f)
110+
if err != nil {
111+
panic(err)
112+
}
113+
114+
return bytes.Split(fileBytes, []byte("\n---"))
115+
}
116+
117+
func extractDeploymentBytes(allK8SResources [][]byte) (deploymentBytes []byte, otherResources [][]byte) {
118+
// find the deployment in the list of resources
119+
for _, resourceBytes := range allK8SResources {
120+
// Because interpreter read only JSON...
121+
resourceJSON, err := k8syaml.ToJSON(resourceBytes)
122+
if err != nil {
123+
panic(err)
124+
}
125+
schema, err := k8sjson.DefaultMetaFactory.Interpret(resourceJSON)
126+
if err != nil {
127+
panic(err)
128+
}
129+
130+
// Is this a deployment or something else
131+
if schema.Kind == "Deployment" {
132+
deploymentBytes = resourceBytes
133+
} else {
134+
otherResources = append(otherResources, resourceBytes)
135+
}
136+
}
137+
138+
if len(deploymentBytes) <= 0 {
139+
panic(errors.New("could not find deployment resource in given file"))
140+
}
141+
142+
return deploymentBytes, otherResources
143+
}
144+
145+
func getCloudContainer() v1.Container {
146+
var cloudSQLProxyContainer v1.Container
147+
{
148+
requestResources, limitResources := setResources(*cpuRequest, *memoryRequest, *cpuLimit, *memoryLimit)
149+
150+
var runAsUser int64 = 2
151+
var allowPrivilegeEscalation = false
152+
153+
securityContext := v1.SecurityContext{
154+
RunAsUser: &runAsUser,
155+
AllowPrivilegeEscalation: &allowPrivilegeEscalation,
156+
}
157+
158+
volumeMount := v1.VolumeMount{
159+
Name: "cloudsql-proxy-credentials",
160+
MountPath: "/secrets/cloudsql",
161+
ReadOnly: true,
162+
}
163+
164+
cloudSQLProxyContainer = v1.Container{}
165+
cloudSQLProxyContainer.Name = "cloudsql-proxy"
166+
cloudSQLProxyContainer.Image = fmt.Sprintf("gcr.io/cloudsql-docker/gce-proxy:%s", *proxyVersion)
167+
cloudSQLProxyContainer.Command = []string{"/cloud_sql_proxy", fmt.Sprintf("-instances=%s:%s:%s", *project, *region, *instance), "-log_debug_stdout=true", fmt.Sprintf("-verbose=%s", *verbose), "-credential_file=/secrets/cloudsql/credentials.json"}
168+
cloudSQLProxyContainer.Resources = v1.ResourceRequirements{Requests: requestResources, Limits: limitResources}
169+
cloudSQLProxyContainer.SecurityContext = &securityContext
170+
cloudSQLProxyContainer.VolumeMounts = append(cloudSQLProxyContainer.VolumeMounts, volumeMount)
171+
}
172+
173+
174+
return cloudSQLProxyContainer
175+
}
176+
177+
// Put the remaining bytes that are not the deployment, back in the output
178+
func putItBack(otherResources [][]byte, w io.Writer) {
179+
for _, resourceBytes := range otherResources {
180+
w.Write([]byte("\n---\n"))
181+
w.Write(resourceBytes)
182+
}
183+
}

main_test.go

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
package main
2+
3+
import (
4+
"bytes"
5+
"github.com/magiconair/properties/assert"
6+
"github.com/stretchr/testify/require"
7+
"io"
8+
"os"
9+
"testing"
10+
)
11+
12+
func Test_runInjector(t *testing.T) {
13+
14+
expectedOutput := `apiVersion: extensions/v1beta1
15+
kind: Deployment
16+
metadata:
17+
creationTimestamp: null
18+
name: test
19+
spec:
20+
replicas: 1
21+
strategy: {}
22+
template:
23+
metadata:
24+
creationTimestamp: null
25+
spec:
26+
containers:
27+
- image: some-image
28+
name: name-test
29+
resources: {}
30+
- command:
31+
- /cloud_sql_proxy
32+
- -instances=project-test:region-test:instance-test
33+
- -log_debug_stdout=true
34+
- -verbose=
35+
- -credential_file=/secrets/cloudsql/credentials.json
36+
image: gcr.io/cloudsql-docker/gce-proxy:1.11
37+
name: cloudsql-proxy
38+
resources:
39+
limits:
40+
cpu: 100m
41+
memory: 128Mi
42+
requests:
43+
cpu: 5m
44+
memory: 8Mi
45+
securityContext:
46+
allowPrivilegeEscalation: false
47+
runAsUser: 2
48+
volumeMounts:
49+
- mountPath: /secrets/cloudsql
50+
name: cloudsql-proxy-credentials
51+
readOnly: true
52+
volumes:
53+
- name: test-volume
54+
secret:
55+
secretName: test-secret
56+
- name: cloudsql-proxy-credentials
57+
secret:
58+
secretName: cloudsql-proxy-credentials
59+
status: {}
60+
61+
---
62+
63+
apiVersion: v1
64+
kind: Service
65+
metadata:
66+
name: test-svc
67+
spec:
68+
ports:
69+
- name: web
70+
port: 8080`
71+
72+
// Just to trick to get control other stdout
73+
// r and w are linked => whatever is written in w is readable in r
74+
oldStdout := os.Stdout
75+
r, w, err := os.Pipe()
76+
require.NoError(t, err)
77+
os.Stdout = w
78+
79+
*path = "./test/test.yaml"
80+
*instance = "instance-test"
81+
*region = "region-test"
82+
*project = "project-test"
83+
*cpuRequest = "5m"
84+
*memoryRequest = "8Mi"
85+
*cpuLimit = "100m"
86+
*memoryLimit = "128Mi"
87+
*proxyVersion = "1.11"
88+
89+
runInjector()
90+
os.Stdout = oldStdout
91+
w.Close()
92+
var buf bytes.Buffer
93+
io.Copy(&buf, r)
94+
assert.Equal(t, expectedOutput, buf.String())
95+
}

test/test.yaml

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
apiVersion: extensions/v1beta1
2+
kind: Deployment
3+
metadata:
4+
name: test
5+
spec:
6+
replicas: 1
7+
template:
8+
spec:
9+
containers:
10+
- name: name-test
11+
image: some-image
12+
volumes:
13+
- name: test-volume
14+
secret:
15+
secretName: test-secret
16+
---
17+
apiVersion: v1
18+
kind: Service
19+
metadata:
20+
name: test-svc
21+
spec:
22+
ports:
23+
- name: web
24+
port: 8080

0 commit comments

Comments
 (0)