blob: d677d6519be7b823d8ed0a9ff87acaa43985e29c [file] [log] [blame]
Matteo Scandoloa4285862020-12-01 18:10:10 -08001/*
2Copyright 2015 The Kubernetes Authors.
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16
17package api
18
19import (
20 "encoding/base64"
21 "errors"
22 "fmt"
23 "io/ioutil"
24 "os"
25 "path"
26 "path/filepath"
27)
28
29func init() {
30 sDec, _ := base64.StdEncoding.DecodeString("REDACTED+")
31 redactedBytes = []byte(string(sDec))
32 sDec, _ = base64.StdEncoding.DecodeString("DATA+OMITTED")
33 dataOmittedBytes = []byte(string(sDec))
34}
35
36// IsConfigEmpty returns true if the config is empty.
37func IsConfigEmpty(config *Config) bool {
38 return len(config.AuthInfos) == 0 && len(config.Clusters) == 0 && len(config.Contexts) == 0 &&
39 len(config.CurrentContext) == 0 &&
40 len(config.Preferences.Extensions) == 0 && !config.Preferences.Colors &&
41 len(config.Extensions) == 0
42}
43
44// MinifyConfig read the current context and uses that to keep only the relevant pieces of config
45// This is useful for making secrets based on kubeconfig files
46func MinifyConfig(config *Config) error {
47 if len(config.CurrentContext) == 0 {
48 return errors.New("current-context must exist in order to minify")
49 }
50
51 currContext, exists := config.Contexts[config.CurrentContext]
52 if !exists {
53 return fmt.Errorf("cannot locate context %v", config.CurrentContext)
54 }
55
56 newContexts := map[string]*Context{}
57 newContexts[config.CurrentContext] = currContext
58
59 newClusters := map[string]*Cluster{}
60 if len(currContext.Cluster) > 0 {
61 if _, exists := config.Clusters[currContext.Cluster]; !exists {
62 return fmt.Errorf("cannot locate cluster %v", currContext.Cluster)
63 }
64
65 newClusters[currContext.Cluster] = config.Clusters[currContext.Cluster]
66 }
67
68 newAuthInfos := map[string]*AuthInfo{}
69 if len(currContext.AuthInfo) > 0 {
70 if _, exists := config.AuthInfos[currContext.AuthInfo]; !exists {
71 return fmt.Errorf("cannot locate user %v", currContext.AuthInfo)
72 }
73
74 newAuthInfos[currContext.AuthInfo] = config.AuthInfos[currContext.AuthInfo]
75 }
76
77 config.AuthInfos = newAuthInfos
78 config.Clusters = newClusters
79 config.Contexts = newContexts
80
81 return nil
82}
83
84var (
85 redactedBytes []byte
86 dataOmittedBytes []byte
87)
88
89// Flatten redacts raw data entries from the config object for a human-readable view.
90func ShortenConfig(config *Config) {
91 // trick json encoder into printing a human readable string in the raw data
92 // by base64 decoding what we want to print. Relies on implementation of
93 // http://golang.org/pkg/encoding/json/#Marshal using base64 to encode []byte
94 for key, authInfo := range config.AuthInfos {
95 if len(authInfo.ClientKeyData) > 0 {
96 authInfo.ClientKeyData = redactedBytes
97 }
98 if len(authInfo.ClientCertificateData) > 0 {
99 authInfo.ClientCertificateData = redactedBytes
100 }
101 if len(authInfo.Token) > 0 {
102 authInfo.Token = "REDACTED"
103 }
104 config.AuthInfos[key] = authInfo
105 }
106 for key, cluster := range config.Clusters {
107 if len(cluster.CertificateAuthorityData) > 0 {
108 cluster.CertificateAuthorityData = dataOmittedBytes
109 }
110 config.Clusters[key] = cluster
111 }
112}
113
114// Flatten changes the config object into a self contained config (useful for making secrets)
115func FlattenConfig(config *Config) error {
116 for key, authInfo := range config.AuthInfos {
117 baseDir, err := MakeAbs(path.Dir(authInfo.LocationOfOrigin), "")
118 if err != nil {
119 return err
120 }
121
122 if err := FlattenContent(&authInfo.ClientCertificate, &authInfo.ClientCertificateData, baseDir); err != nil {
123 return err
124 }
125 if err := FlattenContent(&authInfo.ClientKey, &authInfo.ClientKeyData, baseDir); err != nil {
126 return err
127 }
128
129 config.AuthInfos[key] = authInfo
130 }
131 for key, cluster := range config.Clusters {
132 baseDir, err := MakeAbs(path.Dir(cluster.LocationOfOrigin), "")
133 if err != nil {
134 return err
135 }
136
137 if err := FlattenContent(&cluster.CertificateAuthority, &cluster.CertificateAuthorityData, baseDir); err != nil {
138 return err
139 }
140
141 config.Clusters[key] = cluster
142 }
143
144 return nil
145}
146
147func FlattenContent(path *string, contents *[]byte, baseDir string) error {
148 if len(*path) != 0 {
149 if len(*contents) > 0 {
150 return errors.New("cannot have values for both path and contents")
151 }
152
153 var err error
154 absPath := ResolvePath(*path, baseDir)
155 *contents, err = ioutil.ReadFile(absPath)
156 if err != nil {
157 return err
158 }
159
160 *path = ""
161 }
162
163 return nil
164}
165
166// ResolvePath returns the path as an absolute paths, relative to the given base directory
167func ResolvePath(path string, base string) string {
168 // Don't resolve empty paths
169 if len(path) > 0 {
170 // Don't resolve absolute paths
171 if !filepath.IsAbs(path) {
172 return filepath.Join(base, path)
173 }
174 }
175
176 return path
177}
178
179func MakeAbs(path, base string) (string, error) {
180 if filepath.IsAbs(path) {
181 return path, nil
182 }
183 if len(base) == 0 {
184 cwd, err := os.Getwd()
185 if err != nil {
186 return "", err
187 }
188 base = cwd
189 }
190 return filepath.Join(base, path), nil
191}