-
Notifications
You must be signed in to change notification settings - Fork 159
Add IBMCloud manual mode #356
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
8e856e3
fb49b2a
014ef1b
7696d7c
5843a17
38595a1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| /* | ||
| Copyright 2021 The OpenShift Authors. | ||
|
|
||
| Licensed under the Apache License, Version 2.0 (the "License"); | ||
| you may not use this file except in compliance with the License. | ||
| You may obtain a copy of the License at | ||
|
|
||
| http://www.apache.org/licenses/LICENSE-2.0 | ||
|
|
||
| Unless required by applicable law or agreed to in writing, software | ||
| distributed under the License is distributed on an "AS IS" BASIS, | ||
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| See the License for the specific language governing permissions and | ||
| limitations under the License. | ||
| */ | ||
|
|
||
| package v1 | ||
|
|
||
| import ( | ||
| metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
| ) | ||
|
|
||
| // TODO: these types should eventually be broken out, along with the actuator, to a separate repo. | ||
|
|
||
| // IBMCloudProviderSpec is the specification of the credentials request in IBM Cloud. | ||
| // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object | ||
| type IBMCloudProviderSpec struct { | ||
| metav1.TypeMeta `json:",inline"` | ||
| } | ||
|
|
||
| // IBMCloudProviderStatus contains the status of the IBM Cloud credentials request. | ||
| // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object | ||
| type IBMCloudProviderStatus struct { | ||
| metav1.TypeMeta `json:",inline"` | ||
| } | ||
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,182 @@ | ||
| package ibmcloud | ||
|
|
||
| import ( | ||
| "fmt" | ||
| "io" | ||
| "io/ioutil" | ||
| "log" | ||
| "os" | ||
| "path/filepath" | ||
|
|
||
| "github.com/pkg/errors" | ||
| "github.com/spf13/cobra" | ||
| "k8s.io/apimachinery/pkg/util/yaml" | ||
|
|
||
| credreqv1 "github.com/openshift/cloud-credential-operator/pkg/apis/cloudcredential/v1" | ||
| "github.com/openshift/cloud-credential-operator/pkg/cmd/provisioning" | ||
| ) | ||
|
|
||
| const ( | ||
| secretManifestsTemplate = `apiVersion: v1 | ||
| stringData: | ||
| ibmcloud_api_key: %s | ||
| kind: Secret | ||
| metadata: | ||
| name: %s | ||
| namespace: %s | ||
| type: Opaque` | ||
|
|
||
| manifestsDirName = "manifests" | ||
| ) | ||
|
|
||
| // APIKeyEnvVar is the environment variable name containing an IBM Cloud API key | ||
| const APIKeyEnvVar = "IC_API_KEY" | ||
|
|
||
| var ( | ||
| // CreateOpts captures the options that affect creation of the generated | ||
| // objects. | ||
| CreateOpts = options{ | ||
| TargetDir: "", | ||
| } | ||
| ) | ||
|
|
||
| // NewCreateSecretsCmd implements the "create-secrets" command for the credentials provisioning | ||
| func NewCreateSecretsCmd() *cobra.Command { | ||
| createSecretsCmd := &cobra.Command{ | ||
| Use: "create-secrets", | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @BobbyRadford I was discussing this with the team and I was wondering if a name like Could we rename this to something like
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hey @joelddiaz no problem. Will make that happen. |
||
| Short: "Create credentials objects", | ||
| Long: "Creating objects related to cloud credentials", | ||
| RunE: createSecretsCmd, | ||
| PersistentPreRun: initEnvForCreateCmd, | ||
| } | ||
|
|
||
| createSecretsCmd.PersistentFlags().StringVar(&CreateOpts.CredRequestDir, "credentials-requests-dir", "", "Directory containing files of CredentialsRequests (can be created by running 'oc adm release extract --credentials-requests --cloud=ibmcloud' against an OpenShift release image)") | ||
| createSecretsCmd.MarkPersistentFlagRequired("credentials-requests-dir") | ||
| createSecretsCmd.PersistentFlags().StringVar(&CreateOpts.TargetDir, "output-dir", "", "Directory to place generated files (defaults to current directory)") | ||
|
|
||
| return createSecretsCmd | ||
| } | ||
|
|
||
| func createSecretsCmd(cmd *cobra.Command, args []string) error { | ||
| apiKey := os.Getenv(APIKeyEnvVar) | ||
| if apiKey == "" { | ||
| return fmt.Errorf("%s environment variable not set", APIKeyEnvVar) | ||
| } | ||
|
|
||
| err := createSecrets(CreateOpts.CredRequestDir, CreateOpts.TargetDir, apiKey) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| return nil | ||
| } | ||
|
|
||
| func createSecrets(credReqDir string, targetDir string, apiKey string) error { | ||
| credRequests, err := getListOfCredentialsRequests(credReqDir) | ||
| if err != nil { | ||
| return errors.Wrap(err, "Failed to process files containing CredentialsRequests") | ||
| } | ||
|
|
||
| for _, cr := range credRequests { | ||
| if err := processCredReq(cr, targetDir, apiKey); err != nil { | ||
| return errors.Wrap(err, "Failed to process CredentialsReqeust") | ||
| } | ||
| } | ||
| return nil | ||
| } | ||
|
|
||
| func getListOfCredentialsRequests(dir string) ([]*credreqv1.CredentialsRequest, error) { | ||
| credRequests := []*credreqv1.CredentialsRequest{} | ||
| files, err := ioutil.ReadDir(dir) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
|
|
||
| for _, file := range files { | ||
| f, err := os.Open(filepath.Join(dir, file.Name())) | ||
| if err != nil { | ||
| return nil, errors.Wrap(err, "Failed to open file") | ||
| } | ||
| defer f.Close() | ||
| decoder := yaml.NewYAMLOrJSONDecoder(f, 4096) | ||
| for { | ||
| cr := &credreqv1.CredentialsRequest{} | ||
| if err := decoder.Decode(cr); err != nil { | ||
| if err == io.EOF { | ||
| break | ||
| } | ||
| return nil, errors.Wrap(err, "Failed to decode to CredentialsRequest") | ||
| } | ||
| credRequests = append(credRequests, cr) | ||
| } | ||
|
|
||
| } | ||
|
|
||
| return credRequests, nil | ||
| } | ||
|
|
||
| func processCredReq(cr *credreqv1.CredentialsRequest, targetDir, apiKey string) error { | ||
| // Decode IBMCloudProviderSpec | ||
| codec, err := credreqv1.NewCodec() | ||
| if err != nil { | ||
| return errors.Wrap(err, "Failed to create credReq codec") | ||
| } | ||
|
|
||
| ibmcloudProviderProviderSpec := credreqv1.IBMCloudProviderSpec{} | ||
| if err := codec.DecodeProviderSpec(cr.Spec.ProviderSpec, &ibmcloudProviderProviderSpec); err != nil { | ||
| return errors.Wrap(err, "Failed to decode the provider spec") | ||
| } | ||
|
|
||
| if ibmcloudProviderProviderSpec.Kind != "IBMCloudProviderSpec" { | ||
| return fmt.Errorf("CredentialsRequest %s/%s is not of type IBM Cloud", cr.Namespace, cr.Name) | ||
| } | ||
|
|
||
| return writeCredReqSecret(cr, targetDir, apiKey) | ||
| } | ||
|
|
||
| func writeCredReqSecret(cr *credreqv1.CredentialsRequest, targetDir, apiKey string) error { | ||
| manifestsDir := filepath.Join(targetDir, manifestsDirName) | ||
|
|
||
| fileName := fmt.Sprintf("%s-%s-credentials.yaml", cr.Spec.SecretRef.Namespace, cr.Spec.SecretRef.Name) | ||
| filePath := filepath.Join(manifestsDir, fileName) | ||
|
|
||
| fileData := fmt.Sprintf(secretManifestsTemplate, apiKey, cr.Spec.SecretRef.Name, cr.Spec.SecretRef.Namespace) | ||
|
|
||
| if err := ioutil.WriteFile(filePath, []byte(fileData), 0600); err != nil { | ||
| return errors.Wrap(err, "Failed to save Secret file") | ||
| } | ||
|
|
||
| log.Printf("Saved credentials configuration to: %s", filePath) | ||
|
|
||
| return nil | ||
| } | ||
|
|
||
| // initEnvForCreateCmd will ensure the destination directory is ready to | ||
| // receive the generated files, and will create the directory if necessary. | ||
| func initEnvForCreateCmd(cmd *cobra.Command, args []string) { | ||
| if CreateOpts.TargetDir == "" { | ||
| pwd, err := os.Getwd() | ||
| if err != nil { | ||
| log.Fatalf("Failed to get current directory: %s", err) | ||
| } | ||
|
|
||
| CreateOpts.TargetDir = pwd | ||
| } | ||
|
|
||
| fPath, err := filepath.Abs(CreateOpts.TargetDir) | ||
| if err != nil { | ||
| log.Fatalf("Failed to resolve full path: %s", err) | ||
| } | ||
|
|
||
| // create target dir if necessary | ||
| err = provisioning.EnsureDir(fPath) | ||
| if err != nil { | ||
| log.Fatalf("failed to create target directory at %s", fPath) | ||
| } | ||
|
|
||
| // create manifests dir if necessary | ||
| manifestsDir := filepath.Join(fPath, manifestsDirName) | ||
| err = provisioning.EnsureDir(manifestsDir) | ||
| if err != nil { | ||
| log.Fatalf("failed to create manifests directory at %s", manifestsDir) | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.