github.com/caos/orbos@v1.5.14-0.20221103111702-e6cd0cea7ad4/pkg/secret/rwsecret.go (about) 1 package secret 2 3 import ( 4 "fmt" 5 "sort" 6 "strings" 7 8 "github.com/caos/orbos/pkg/helper" 9 10 "github.com/caos/orbos/pkg/git" 11 12 v1 "k8s.io/api/core/v1" 13 mach "k8s.io/apimachinery/pkg/apis/meta/v1" 14 15 macherrs "k8s.io/apimachinery/pkg/api/errors" 16 17 "github.com/caos/orbos/pkg/kubernetes" 18 19 "github.com/AlecAivazis/survey/v2" 20 21 "github.com/caos/orbos/pkg/tree" 22 23 "github.com/caos/orbos/mntr" 24 ) 25 26 type PushFuncs func(trees map[string]*tree.Tree, path string) error 27 type GetFuncs func() (map[string]*Secret, map[string]*Existing, map[string]*tree.Tree, error) 28 29 func Read( 30 k8sClient kubernetes.ClientInt, 31 path string, 32 getFunc GetFuncs, 33 ) ( 34 val string, 35 err error, 36 ) { 37 38 defer func() { 39 if err != nil { 40 err = fmt.Errorf("reading secret failed: %w", err) 41 } 42 }() 43 44 allSecrets, allExisting, _, err := getFunc() 45 if err != nil { 46 return "", err 47 } 48 if helper.IsNil(k8sClient) { 49 allExisting = make(map[string]*Existing) 50 } 51 52 /* 53 if allSecrets == nil || len(allSecrets) == 0 { 54 return "", errors.New("no secrets found") 55 } 56 */ 57 58 secret, err := findSecret(allSecrets, allExisting, &path, false) 59 if err != nil { 60 return "", err 61 } 62 63 switch secretType := secret.(type) { 64 case *Secret: 65 if secretType.Value == "" { 66 return "", fmt.Errorf("secret %s is empty", path) 67 } 68 return secretType.Value, nil 69 case *Existing: 70 if secretType.Name == "" { 71 return "", fmt.Errorf("secret %s has no name specified", path) 72 } 73 if secretType.Key == "" { 74 return "", fmt.Errorf("secret %s has no key specified", path) 75 } 76 k8sSecret, err := k8sClient.GetSecret(existingSecretsNamespace, secretType.Name) 77 if err != nil { 78 return "", err 79 } 80 bytes, ok := k8sSecret.Data[secretType.Key] 81 if !ok || len(bytes) == 0 { 82 return "", fmt.Errorf("Kubernetes secret is empty at key %s", secretType.Key) 83 } 84 return string(bytes), nil 85 } 86 panic(fmt.Errorf("unknown secret of type %T", secret)) 87 } 88 89 func Rewrite( 90 newMasterKey string, 91 pushFunc func() error, 92 ) error { 93 oldMasterKey := Masterkey 94 Masterkey = newMasterKey 95 defer func() { 96 Masterkey = oldMasterKey 97 }() 98 99 return pushFunc() 100 } 101 102 func Write( 103 monitor mntr.Monitor, 104 k8sClient kubernetes.ClientInt, 105 path, 106 value, 107 writtenByCLI, 108 writtenByVersion string, 109 getFunc GetFuncs, 110 pushFunc PushFuncs, 111 ) error { 112 allSecrets, allExisting, allTrees, err := getFunc() 113 if err != nil { 114 return err 115 } 116 117 if helper.IsNil(k8sClient) { 118 allExisting = make(map[string]*Existing) 119 } 120 121 secret, err := findSecret(allSecrets, allExisting, &path, true) 122 if err != nil { 123 return err 124 } 125 126 switch secretType := secret.(type) { 127 case *Secret: 128 if secretType.Value == value { 129 monitor.Info("Value is unchanged") 130 return nil 131 } 132 secretType.Value = value 133 case *Existing: 134 var refChanged bool 135 if secretType.Name == "" { 136 secretType.Name = strings.ReplaceAll(path, ".", "-") 137 refChanged = true 138 } 139 140 if secretType.Key == "" { 141 secretType.Key = "default" 142 refChanged = true 143 } 144 145 k8sSecret, err := k8sClient.GetSecret(existingSecretsNamespace, secretType.Name) 146 if macherrs.IsNotFound(err) { 147 err = nil 148 k8sSecret = &v1.Secret{ 149 ObjectMeta: mach.ObjectMeta{ 150 Name: secretType.Name, 151 Namespace: existingSecretsNamespace, 152 Labels: map[string]string{ 153 "cli": writtenByCLI, 154 "version": writtenByVersion, 155 }, 156 }, 157 Immutable: boolPtr(false), 158 Type: v1.SecretTypeOpaque, 159 } 160 } 161 if err != nil { 162 return err 163 } 164 165 if k8sSecret.Data == nil { 166 k8sSecret.Data = make(map[string][]byte) 167 } 168 k8sSecret.Data[secretType.Key] = []byte(value) 169 if err := k8sClient.ApplySecret(k8sSecret); err != nil { 170 return err 171 } 172 if !refChanged { 173 return nil 174 } 175 } 176 177 return pushFunc(allTrees, path) 178 } 179 180 func GetOperatorSecrets( 181 monitor mntr.Monitor, 182 printLogs, 183 gitops bool, 184 gitClient *git.Client, 185 desiredFile git.DesiredFile, 186 allTrees map[string]*tree.Tree, 187 allSecrets map[string]*Secret, 188 allExistingSecrets map[string]*Existing, 189 treeFromCRD func() (*tree.Tree, error), 190 getOperatorSpecifics func(*tree.Tree) (map[string]*Secret, map[string]*Existing, bool, error), 191 ) error { 192 193 operator := strings.Split(string(desiredFile), ".")[0] 194 195 if gitops { 196 if !gitClient.Exists(desiredFile) { 197 if printLogs { 198 monitor.Info(fmt.Sprintf("file %s not found", desiredFile)) 199 } 200 return nil 201 } 202 203 operatorTree, err := gitClient.ReadTree(desiredFile) 204 if err != nil { 205 return err 206 } 207 allTrees[operator] = operatorTree 208 } else { 209 operatorTree, err := treeFromCRD() 210 if operatorTree == nil { 211 return err 212 } 213 allTrees[operator] = operatorTree 214 } 215 216 secrets, existing, migrate, err := getOperatorSpecifics(allTrees[operator]) 217 if err != nil { 218 return err 219 } 220 221 if migrate { 222 return fmt.Errorf("please use the api command to migrate to the latest %s api first", operator) 223 } 224 225 if !gitops { 226 secrets = nil 227 } 228 229 suffixedSecrets := make(map[string]*Secret, len(secrets)) 230 suffixedExisting := make(map[string]*Existing, len(existing)) 231 for k, v := range secrets { 232 suffixedSecrets[k+".encrypted"] = v 233 } 234 for k, v := range existing { 235 suffixedExisting[k+".existing"] = v 236 } 237 238 AppendSecrets(operator, allSecrets, suffixedSecrets, allExistingSecrets, suffixedExisting) 239 240 return nil 241 } 242 243 func secretsListToSlice( 244 secrets map[string]*Secret, 245 existing map[string]*Existing, 246 includeEmpty bool, 247 ) []string { 248 items := make([]string, 0, len(secrets)+len(existing)) 249 for key, value := range secrets { 250 if includeEmpty || (value != nil && value.Value != "") { 251 items = append(items, key) 252 } 253 } 254 for key, value := range existing { 255 if includeEmpty || (value != nil && value.Name != "" && value.Key != "") { 256 items = append(items, key) 257 } 258 } 259 return items 260 } 261 262 func findSecret( 263 allSecrets map[string]*Secret, 264 allExisting map[string]*Existing, 265 path *string, 266 includeEmpty bool, 267 ) ( 268 interface{}, 269 error, 270 ) { 271 if *path != "" { 272 secret, err := exactSecret(allSecrets, allExisting, *path) 273 return secret, mntr.ToUserError(err) 274 } 275 276 selectItems := secretsListToSlice(allSecrets, allExisting, includeEmpty) 277 278 sort.Slice(selectItems, func(i, j int) bool { 279 iDots := strings.Count(selectItems[i], ".") 280 jDots := strings.Count(selectItems[j], ".") 281 return iDots < jDots || iDots == jDots && selectItems[i] < selectItems[j] 282 }) 283 284 var result string 285 if err := survey.AskOne(&survey.Select{ 286 Message: "Select a secret:", 287 Options: selectItems, 288 }, &result, survey.WithValidator(survey.Required)); err != nil { 289 return nil, err 290 } 291 *path = result 292 293 secret, err := exactSecret(allSecrets, allExisting, *path) 294 if err != nil { 295 panic(err) 296 } 297 return secret, nil 298 } 299 300 func exactSecret( 301 secrets map[string]*Secret, 302 existings map[string]*Existing, 303 path string, 304 ) ( 305 interface{}, 306 error, 307 ) { 308 secret, ok := secrets[path] 309 if ok { 310 return secret, nil 311 } 312 313 existing, ok := existings[path] 314 if ok { 315 return existing, nil 316 } 317 318 return nil, fmt.Errorf("no secret found at %s", path) 319 } 320 321 func boolPtr(b bool) *bool { return &b }