providers/ibm/cloud_functions.go (197 lines of code) (raw):

// Copyright 2019 The Terraformer 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 ibm import ( "fmt" "net/http" "net/url" "os" "strings" "github.com/GoogleCloudPlatform/terraformer/terraformutils" bluemix "github.com/IBM-Cloud/bluemix-go" ns "github.com/IBM-Cloud/bluemix-go/api/functions" "github.com/IBM-Cloud/bluemix-go/session" "github.com/apache/openwhisk-client-go/whisk" ) // CloudFunctionGenerator .. type CloudFunctionGenerator struct { IBMService } func (g CloudFunctionGenerator) loadPackages(namespace, pkgName string) terraformutils.Resource { resource := terraformutils.NewResource( fmt.Sprintf("%s:%s", namespace, pkgName), normalizeResourceName(fmt.Sprintf("%s_%s", namespace, pkgName), false), "ibm_function_package", "ibm", map[string]string{}, []string{}, map[string]interface{}{}) return resource } func (g CloudFunctionGenerator) loadRules(namespace, ruleName string) terraformutils.Resource { resource := terraformutils.NewResource( fmt.Sprintf("%s:%s", namespace, ruleName), normalizeResourceName(ruleName, true), "ibm_function_rule", "ibm", map[string]string{}, []string{}, map[string]interface{}{}) return resource } func (g CloudFunctionGenerator) loadTriggers(namespace, triggerName string) terraformutils.Resource { resource := terraformutils.NewResource( fmt.Sprintf("%s:%s", namespace, triggerName), normalizeResourceName(triggerName, true), "ibm_function_trigger", "ibm", map[string]string{}, []string{}, map[string]interface{}{}) return resource } /* * * Configure a HTTP client using the OpenWhisk properties (i.e. host, auth, iamtoken) * Only cf-based namespaces needs auth key value. * iam-based namespace don't have an auth key and needs only iam token for authorization. * */ func setupOpenWhiskClientConfigIAM(response ns.NamespaceResponse, c *bluemix.Config, region string) (*whisk.Client, error) { u, _ := url.Parse(fmt.Sprintf("https://%s.functions.cloud.ibm.com/api", region)) wskClient, _ := whisk.NewClient(http.DefaultClient, &whisk.Config{ Host: u.Host, Version: "v1", }) if os.Getenv("TF_LOG") != "" { whisk.SetDebug(true) } // Configure whisk properties to handle iam-based/iam-migrated namespaces. if response.IsIamEnabled() { additionalHeaders := make(http.Header) additionalHeaders.Add("Authorization", c.IAMAccessToken) additionalHeaders.Add("X-Namespace-Id", response.GetID()) wskClient.Config.Namespace = response.GetID() wskClient.Config.AdditionalHeaders = additionalHeaders return wskClient, nil } return nil, fmt.Errorf("Failed to create whisk config object for IAM based namespace '%v'", response.GetName()) } // InitResources .. func (g *CloudFunctionGenerator) InitResources() error { region := g.Args["region"].(string) bmxConfig := &bluemix.Config{ BluemixAPIKey: os.Getenv("IC_API_KEY"), } bmxConfig.Region = region sess, err := session.New(bmxConfig) if err != nil { return err } err = authenticateAPIKey(sess) if err != nil { return err } err = authenticateCF(sess) if err != nil { return err } nsClient, err := ns.New(sess) if err != nil { return err } nsList, err := nsClient.Namespaces().GetNamespaces() if err != nil { return nil } for _, n := range nsList.Namespaces { // Namespace if !n.IsIamEnabled() { continue } // Build whisk object wskClient, err := setupOpenWhiskClientConfigIAM(n, sess.Config, region) if err != nil { return err } // Package packageService := wskClient.Packages pkgOptions := &whisk.PackageListOptions{ Limit: 100, Skip: 0, } pkgs, _, err := packageService.List(pkgOptions) if err != nil { return fmt.Errorf("Error retrieving IBM Cloud Function package: %s", err) } for _, p := range pkgs { g.Resources = append(g.Resources, g.loadPackages(n.GetName(), p.GetName())) } // Action actionService := wskClient.Actions actionOptions := &whisk.ActionListOptions{ Limit: 100, Skip: 0, } actions, _, err := actionService.List("", actionOptions) if err != nil { return fmt.Errorf("Error retrieving IBM Cloud Function action: %s", err) } for _, a := range actions { actionID := "" parts := strings.Split(a.Namespace, "/") if len(parts) == 2 { var pkgDependsOn []string pkgDependsOn = append(pkgDependsOn, "ibm_function_package."+terraformutils.TfSanitize(fmt.Sprintf("%s_%s", n.GetName(), parts[1]))) actionID = fmt.Sprintf("%s/%s", parts[1], a.Name) g.Resources = append(g.Resources, terraformutils.NewResource( fmt.Sprintf("%s:%s", n.GetName(), actionID), normalizeResourceName(a.Name, true), "ibm_function_action", "ibm", map[string]string{}, []string{}, map[string]interface{}{ "depends_on": pkgDependsOn, })) } else { g.Resources = append(g.Resources, terraformutils.NewResource( fmt.Sprintf("%s:%s", n.GetName(), a.Name), normalizeResourceName(a.Name, true), "ibm_function_action", "ibm", map[string]string{}, []string{}, map[string]interface{}{})) } } // Rule ruleService := wskClient.Rules ruleOptions := &whisk.RuleListOptions{ Limit: 100, Skip: 0, } rules, _, err := ruleService.List(ruleOptions) if err != nil { return fmt.Errorf("Error retrieving IBM Cloud Function rule: %s", err) } for _, r := range rules { g.Resources = append(g.Resources, g.loadRules(n.GetName(), r.Name)) } // Triggers triggerService := wskClient.Triggers triggerOptions := &whisk.TriggerListOptions{ Limit: 100, Skip: 0, } triggers, _, err := triggerService.List(triggerOptions) if err != nil { return fmt.Errorf("Error retrieving IBM Cloud Function trigger: %s", err) } for _, t := range triggers { g.Resources = append(g.Resources, g.loadTriggers(n.GetName(), t.Name)) } } return nil } func (g *CloudFunctionGenerator) PostConvertHook() error { for i, r := range g.Resources { if r.InstanceInfo.Type != "ibm_function_action" { continue } for _, ri := range g.Resources { if ri.InstanceInfo.Type != "ibm_function_package" { continue } if len(strings.Split(r.InstanceState.Attributes["id"], "/")) == 2 { if strings.Split(r.InstanceState.Attributes["id"], "/")[0] == ri.InstanceState.Attributes["id"] { g.Resources[i].Item["name"] = "${ibm_function_package." + ri.ResourceName + ".name}" + "/" + r.InstanceState.Attributes["action_id"] } } } } return nil }