-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathmain.go
244 lines (197 loc) · 6.55 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
package main
import (
"errors"
"flag"
"fmt"
"log"
"os"
"path/filepath"
"strings"
"gopkg.in/yaml.v3"
)
type KubectlConfig struct {
Kind string `yaml:"kind"`
ApiVersion string `yaml:"apiVersion"`
CurrentContext string `yaml:"current-context"`
Clusters []*KubectlClusterWithName `yaml:"clusters"`
Contexts []*KubectlContextWithName `yaml:"contexts"`
Users []*KubectlUserWithName `yaml:"users"`
}
type KubectlUser struct {
ClientCertificateData string `yaml:"client-certificate-data,omitempty"`
ClientKeyData string `yaml:"client-key-data,omitempty"`
Password string `yaml:"password,omitempty"`
Username string `yaml:"username,omitempty"`
Token string `yaml:"token,omitempty"`
}
type KubectlUserWithName struct {
Name string `yaml:"name"`
User KubectlUser `yaml:"user"`
}
type KubectlContext struct {
Cluster string `yaml:"cluster"`
User string `yaml:"user"`
}
type KubectlContextWithName struct {
Name string `yaml:"name"`
Context KubectlContext `yaml:"context"`
}
type KubectlCluster struct {
Server string `yaml:"server"`
CertificateAuthorityData string `yaml:"certificate-authority-data,omitempty"`
}
type KubectlClusterWithName struct {
Name string `yaml:"name"`
Cluster KubectlCluster `yaml:"cluster"`
}
const KUBECONFIG_ENV = "KUBECONFIG"
const KUBECONFIG_ENV_KEY = "$KUBECONFIG"
var KUBECONFIG_DEFAULT_PATH = func() string {
home, err := os.UserHomeDir()
if err != nil {
log.Panic(err)
}
return filepath.Join(home, ".kube", "config")
}()
func ParseKubeConfig(path string) (*KubectlConfig, error) {
file, err := os.ReadFile(path)
if err != nil {
return nil, err
}
var kubeConfig KubectlConfig
err = yaml.Unmarshal(file, &kubeConfig)
if err != nil {
return nil, err
}
return &kubeConfig, nil
}
func ValidateOnlyOneContext(toBeAppend KubectlConfig, kubectlConfig KubectlConfig) error {
if len(toBeAppend.Clusters) != 1 {
return errors.New("only one cluster can be merged into original kubeconfig")
}
if len(toBeAppend.Users) != 1 {
return errors.New("only one user can be merged into original kubeconfig")
}
if len(toBeAppend.Contexts) != 1 {
return errors.New("only one context can be merged into original kubeconfig")
}
return nil
}
func ValidateDuplication(toBeAppend KubectlConfig, kubectlConfig KubectlConfig) error {
for _, v1 := range kubectlConfig.Clusters {
for _, v2 := range toBeAppend.Clusters {
if strings.EqualFold(v1.Name, v2.Name) {
return fmt.Errorf("a cluster entry with %s already exists in kubeconfig, merge failed", v1.Name)
}
}
}
for _, v1 := range kubectlConfig.Users {
for _, v2 := range toBeAppend.Users {
if strings.EqualFold(v1.Name, v2.Name) {
return fmt.Errorf("a user entry with %s already exists in kubeconfig, merge failed", v1.Name)
}
}
}
for _, v1 := range kubectlConfig.Contexts {
for _, v2 := range toBeAppend.Contexts {
if strings.EqualFold(v1.Name, v2.Name) {
return fmt.Errorf("a context entry with %s already exists in kubeconfig, merge failed", v1.Name)
}
}
}
return nil
}
func Merge(kubeConfig KubectlConfig, toBeAppend KubectlConfig, name string, override bool) (*KubectlConfig, error) {
var err = ValidateOnlyOneContext(toBeAppend, kubeConfig)
if err != nil {
return nil, err
}
toBeAppend.Clusters[0].Name = name
toBeAppend.Users[0].Name = name
toBeAppend.Contexts[0].Name = name
toBeAppend.Contexts[0].Context.Cluster = name
toBeAppend.Contexts[0].Context.User = name
var needOverride = false
err = ValidateDuplication(toBeAppend, kubeConfig)
if err != nil {
if !override {
return nil, err
}
needOverride = true
}
if needOverride {
// remove the existing cluster, user and context
for i, v := range kubeConfig.Clusters {
if strings.EqualFold(v.Name, name) {
kubeConfig.Clusters = append(kubeConfig.Clusters[:i], kubeConfig.Clusters[i+1:]...)
break
}
}
for i, v := range kubeConfig.Users {
if strings.EqualFold(v.Name, name) {
kubeConfig.Users = append(kubeConfig.Users[:i], kubeConfig.Users[i+1:]...)
break
}
}
for i, v := range kubeConfig.Contexts {
if strings.EqualFold(v.Name, name) {
kubeConfig.Contexts = append(kubeConfig.Contexts[:i], kubeConfig.Contexts[i+1:]...)
break
}
}
fmt.Printf("Cluster, context and user with '%s' name is removed because of override flag\n", name)
}
kubeConfig.Clusters = append(kubeConfig.Clusters, toBeAppend.Clusters[0])
kubeConfig.Users = append(kubeConfig.Users, toBeAppend.Users[0])
kubeConfig.Contexts = append(kubeConfig.Contexts, toBeAppend.Contexts[0])
fmt.Printf("Cluster, context and user will be added with '%s' name\n", name)
return &kubeConfig, nil
}
func getKubeConfigPath(passedValue string) string {
//--kubeconfig is not passed
kubeConfigPath := strings.ReplaceAll(passedValue, " ", "")
if len(kubeConfigPath) == 0 {
log.Printf("--kubeconfig was not passed. Looking the %s environment variable\n", KUBECONFIG_ENV)
// case1: env variable exists
kubeConfigPath = os.Getenv(KUBECONFIG_ENV)
// case2: fallback to default path
if len(kubeConfigPath) == 0 {
log.Printf("%s env variable does not exist. Default %s path will be used\n", KUBECONFIG_ENV, KUBECONFIG_DEFAULT_PATH)
}
}
return kubeConfigPath
}
func main() {
kubeConfigPtr := flag.String("kubeconfig", "", fmt.Sprintf("path to the kubeconfig file (defaults '%s' or '%s')", KUBECONFIG_ENV_KEY, KUBECONFIG_DEFAULT_PATH))
filePtr := flag.String("file", "", "path to the yaml file that to be append into kubeconfig")
overridePtr := flag.Bool("override", false, "Override the existing context, user and cluster with the file name, or the fields in the file will be used")
flag.Parse()
var kubeConfigPath = getKubeConfigPath(*kubeConfigPtr)
kubeConfig, err := ParseKubeConfig(kubeConfigPath)
if err != nil {
log.Panic(err)
}
if filepath.Ext(*filePtr) == "" {
log.Panic("the file specified by --file must have a valid extension")
}
toBeAppend, err := ParseKubeConfig(*filePtr)
if err != nil {
log.Panic(err)
}
var fileName = filepath.Base(*filePtr)
fileName = strings.TrimSuffix(fileName, filepath.Ext(fileName))
fileName = strings.ToLower(fileName)
result, err := Merge(*kubeConfig, *toBeAppend, fileName, *overridePtr)
if err != nil {
log.Panic(err)
}
data, err := yaml.Marshal(result)
if err != nil {
log.Panic(err)
}
err = os.WriteFile(kubeConfigPath, data, 0644)
if err != nil {
log.Panic(err)
}
log.Printf("%s was modified successfully\n", kubeConfigPath)
}