blob: 65a36936b57b8b66591f1209a96053c46a237147 [file] [log] [blame]
Scott Bakere7144bc2019-10-01 14:16:47 -07001/*
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 config.AuthInfos[key] = authInfo
102 }
103 for key, cluster := range config.Clusters {
104 if len(cluster.CertificateAuthorityData) > 0 {
105 cluster.CertificateAuthorityData = dataOmittedBytes
106 }
107 config.Clusters[key] = cluster
108 }
109}
110
111// Flatten changes the config object into a self contained config (useful for making secrets)
112func FlattenConfig(config *Config) error {
113 for key, authInfo := range config.AuthInfos {
114 baseDir, err := MakeAbs(path.Dir(authInfo.LocationOfOrigin), "")
115 if err != nil {
116 return err
117 }
118
119 if err := FlattenContent(&authInfo.ClientCertificate, &authInfo.ClientCertificateData, baseDir); err != nil {
120 return err
121 }
122 if err := FlattenContent(&authInfo.ClientKey, &authInfo.ClientKeyData, baseDir); err != nil {
123 return err
124 }
125
126 config.AuthInfos[key] = authInfo
127 }
128 for key, cluster := range config.Clusters {
129 baseDir, err := MakeAbs(path.Dir(cluster.LocationOfOrigin), "")
130 if err != nil {
131 return err
132 }
133
134 if err := FlattenContent(&cluster.CertificateAuthority, &cluster.CertificateAuthorityData, baseDir); err != nil {
135 return err
136 }
137
138 config.Clusters[key] = cluster
139 }
140
141 return nil
142}
143
144func FlattenContent(path *string, contents *[]byte, baseDir string) error {
145 if len(*path) != 0 {
146 if len(*contents) > 0 {
147 return errors.New("cannot have values for both path and contents")
148 }
149
150 var err error
151 absPath := ResolvePath(*path, baseDir)
152 *contents, err = ioutil.ReadFile(absPath)
153 if err != nil {
154 return err
155 }
156
157 *path = ""
158 }
159
160 return nil
161}
162
163// ResolvePath returns the path as an absolute paths, relative to the given base directory
164func ResolvePath(path string, base string) string {
165 // Don't resolve empty paths
166 if len(path) > 0 {
167 // Don't resolve absolute paths
168 if !filepath.IsAbs(path) {
169 return filepath.Join(base, path)
170 }
171 }
172
173 return path
174}
175
176func MakeAbs(path, base string) (string, error) {
177 if filepath.IsAbs(path) {
178 return path, nil
179 }
180 if len(base) == 0 {
181 cwd, err := os.Getwd()
182 if err != nil {
183 return "", err
184 }
185 base = cwd
186 }
187 return filepath.Join(base, path), nil
188}