-
Notifications
You must be signed in to change notification settings - Fork 1.8k
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
Cloud functions #899
Cloud functions #899
Changes from 28 commits
eedd890
5c394fa
93f5856
8288741
c74e219
70c6fe9
c169968
b0939d0
6fd1f58
28ca0a8
127b69c
463af45
0c52870
e65ab5f
0180d8d
3696812
859ed3b
94c2e50
361691d
898cfad
5387b18
992b7a8
ea2abdb
45cace1
6623728
2e31b1d
02d5847
ee5da4a
ea9f204
172564a
3d13310
d9a36e8
f1e6cec
f466669
ea526b3
79db2cf
1478794
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,97 @@ | ||
package google | ||
|
||
import ( | ||
"fmt" | ||
"regexp" | ||
"strconv" | ||
"strings" | ||
) | ||
|
||
const ( | ||
CLOUDFUNCTIONS_FULL_NAME = 0 | ||
CLOUDFUNCTIONS_REGION_ONLY = 1 | ||
) | ||
|
||
//Function would return formatted string to be used in API calls for name or location | ||
// Arguments: | ||
// funcType: {CLOUDFUNCTIONS_REGION_ONLY, CLOUDFUNCTIONS_FULL_NAME} | ||
// If specifying CLOUDFUNCTIONS_FULL_NAME string would be in format 'projects/YOUR_PROJECT/locations/REGION/functions/FUNCTION_NAME' | ||
// If specifying CLOUDFUNCTIONS_REGION_ONLY string would be in format 'projects/YOUR_PROJECT/locations/REGION' | ||
// projectName: Name of project in Google Cloud | ||
// region: In which region function is/should be located. NOTE: Not all regions are supported in 2017 | ||
// funcName: name of function. In case CLOUDFUNCTIONS_REGION_ONLY might empty | ||
// Returns: | ||
// path to function | ||
func createCloudFunctionsPathString(funcType int, projectName string, region string, funcName string) (path string) { | ||
path = fmt.Sprintf("projects/%s/locations/%s", projectName, region) | ||
if funcType == CLOUDFUNCTIONS_FULL_NAME { | ||
path = fmt.Sprintf("%s/functions/%s", path, funcName) | ||
} | ||
return | ||
} | ||
|
||
//Function would extract short function name from long used in Google cloud | ||
//Arguments: | ||
// fullPath: function name in Google cloud | ||
//Return: | ||
// functionName: string short function name | ||
// error: Error if fullPath is not correct Google cloud function name | ||
func getCloudFunctionName(fullPath string) (string, error) { | ||
allParts, err := splitCloudFunctionFullPath(fullPath) | ||
if err != nil { | ||
return "", err | ||
} | ||
return allParts[3], nil | ||
} | ||
|
||
//Function would extract region from long used in Google cloud | ||
//Arguments: | ||
// fullPath: function name in Google cloud | ||
//Return: | ||
// region: string zone in which function is deployed | ||
// error: Error if fullPath is not correct Google cloud function name | ||
func getCloudFunctionRegion(fullPath string) (string, error) { | ||
allParts, err := splitCloudFunctionFullPath(fullPath) | ||
if err != nil { | ||
return "", err | ||
} | ||
return allParts[2], nil | ||
} | ||
|
||
//Function would extract project from long used in Google cloud | ||
//Arguments: | ||
// fullPath: function name in Google cloud | ||
//Return: | ||
// project: string project in which function is deployed | ||
// error: Error if fullPath is not correct Google cloud function name | ||
func getCloudFunctionProject(fullPath string) (string, error) { | ||
allParts, err := splitCloudFunctionFullPath(fullPath) | ||
if err != nil { | ||
return "", err | ||
} | ||
return allParts[1], nil | ||
} | ||
|
||
//Function would split full CloudFunction Path into array of fullName,project,region,funcName | ||
//Arguments: | ||
// fullPath: function name in Google cloud | ||
//Return: | ||
// parts: array of strings which would include fullName,project,region,funcName | ||
// error: Error if fullPath is not correct Google cloud function name | ||
func splitCloudFunctionFullPath(fullPath string) ([]string, error) { | ||
namePattern := regexp.MustCompile("^projects/([^/]+)/locations/([^/]+)/functions/([^/]+)$") | ||
if !namePattern.MatchString(fullPath) { | ||
return nil, fmt.Errorf("%s is not valid CloudFunction full name", fullPath) | ||
} | ||
return namePattern.FindStringSubmatch(fullPath), nil | ||
} | ||
|
||
//Function would read timeout value from GCloud | ||
//Arguments: | ||
// timeout: Timeout in string | ||
//Return: | ||
// timeout int | ||
func readTimeout(s string) (int, error) { | ||
sRemoved := strings.Replace(s, "s", "", -1) | ||
return strconv.Atoi(sRemoved) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
package google | ||
|
||
import ( | ||
"fmt" | ||
"log" | ||
"time" | ||
|
||
"github.com/hashicorp/terraform/helper/resource" | ||
"google.golang.org/api/cloudfunctions/v1" | ||
) | ||
|
||
type CloudFunctionsOperationWaiter struct { | ||
Service *cloudfunctions.Service | ||
Op *cloudfunctions.Operation | ||
} | ||
|
||
func (w *CloudFunctionsOperationWaiter) RefreshFunc() resource.StateRefreshFunc { | ||
return func() (interface{}, string, error) { | ||
var op *cloudfunctions.Operation | ||
var err error | ||
|
||
op, err = w.Service.Operations.Get(w.Op.Name).Do() | ||
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. you can use := here so you don't have to define the two vars above 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. Done |
||
|
||
if err != nil { | ||
return nil, "", err | ||
} | ||
|
||
status := "PENDING" | ||
if op.Done == true { | ||
status = "DONE" | ||
} | ||
|
||
log.Printf("[DEBUG] Got %q when asking for operation %q", status, w.Op.Name) | ||
return op, status, nil | ||
} | ||
} | ||
|
||
func (w *CloudFunctionsOperationWaiter) Conf() *resource.StateChangeConf { | ||
return &resource.StateChangeConf{ | ||
Pending: []string{"PENDING", "RUNNING"}, | ||
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. Is "RUNNING" actually a possible state? It looks like you manually set either "PENDING" or "DONE" 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. Fixed |
||
Target: []string{"DONE"}, | ||
Refresh: w.RefreshFunc(), | ||
} | ||
} | ||
|
||
func cloudFunctionsOperationWait(client *cloudfunctions.Service, | ||
op *cloudfunctions.Operation, activity string) error { | ||
return cloudFunctionsOperationWaitTime(client, op, activity, 4) | ||
} | ||
|
||
func cloudFunctionsOperationWaitTime(client *cloudfunctions.Service, op *cloudfunctions.Operation, | ||
activity string, timeoutMin int) error { | ||
w := &CloudFunctionsOperationWaiter{ | ||
Service: client, | ||
Op: op, | ||
} | ||
|
||
state := w.Conf() | ||
state.Delay = 10 * time.Second | ||
state.Timeout = time.Duration(timeoutMin) * time.Minute | ||
state.MinTimeout = 2 * time.Second | ||
opRaw, err := state.WaitForState() | ||
if err != nil { | ||
return fmt.Errorf("Error waiting for %s: %s", activity, err) | ||
} | ||
|
||
resultOp := opRaw.(*cloudfunctions.Operation) | ||
if resultOp.Error != nil { | ||
return fmt.Errorf(resultOp.Error.Message) | ||
} | ||
|
||
return nil | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,153 @@ | ||
package google | ||
|
||
import ( | ||
"strings" | ||
|
||
"github.com/hashicorp/terraform/helper/schema" | ||
) | ||
|
||
func dataSourceGoogleCloudFunctionsFunction() *schema.Resource { | ||
return &schema.Resource{ | ||
Read: dataSourceGoogleCloudFunctionsFunctionRead, | ||
|
||
Schema: map[string]*schema.Schema{ | ||
"name": { | ||
Type: schema.TypeString, | ||
Required: true, | ||
}, | ||
|
||
"description": { | ||
Type: schema.TypeString, | ||
Optional: true, | ||
Computed: true, | ||
}, | ||
|
||
"entry_point": { | ||
Type: schema.TypeString, | ||
Optional: true, | ||
Computed: true, | ||
}, | ||
|
||
"memory": { | ||
Type: schema.TypeInt, | ||
Optional: true, | ||
Computed: true, | ||
}, | ||
|
||
"region": { | ||
Type: schema.TypeString, | ||
Optional: true, | ||
Computed: true, | ||
}, | ||
|
||
"timeout": { | ||
Type: schema.TypeInt, | ||
Optional: true, | ||
Computed: true, | ||
}, | ||
|
||
"storage_bucket": { | ||
Type: schema.TypeString, | ||
Optional: true, | ||
Computed: true, | ||
}, | ||
|
||
"storage_object": { | ||
Type: schema.TypeString, | ||
Optional: true, | ||
Computed: true, | ||
}, | ||
|
||
"trigger_bucket": { | ||
Type: schema.TypeString, | ||
Optional: true, | ||
Computed: true, | ||
}, | ||
|
||
"trigger_http": { | ||
Type: schema.TypeBool, | ||
Optional: true, | ||
Computed: true, | ||
}, | ||
|
||
"trigger_topic": { | ||
Type: schema.TypeString, | ||
Optional: true, | ||
Computed: true, | ||
}, | ||
|
||
"project": { | ||
Type: schema.TypeString, | ||
Optional: true, | ||
Computed: true, | ||
}, | ||
}, | ||
} | ||
} | ||
|
||
func dataSourceGoogleCloudFunctionsFunctionRead(d *schema.ResourceData, meta interface{}) error { | ||
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. Is this significantly different from the resource read function? If not, you should just call that one from this to save on some duplicated code. You might also be able to do a similar thing for the schema- check out data_source_google_container_cluster.go for an example of a data source that utilitzes the code from the resource really well. 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. Fixed, now code looks cleaner - thank you for heads up :) |
||
config := meta.(*Config) | ||
|
||
service := config.clientCloudFunctions | ||
|
||
project, err := getProject(d, config) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
region, err := getRegion(d, config) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
name := d.Get("name").(string) | ||
|
||
getOpt, err := service.Projects.Locations.Functions.Get( | ||
createCloudFunctionsPathString(CLOUDFUNCTIONS_FULL_NAME, project, region, name)).Do() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
funcRegion, err := getCloudFunctionRegion(getOpt.Name) | ||
if err != nil { | ||
return err | ||
} | ||
funcProject, err := getCloudFunctionProject(getOpt.Name) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
d.Set("description", getOpt.Description) | ||
d.Set("entry_point", getOpt.EntryPoint) | ||
d.Set("memory", getOpt.AvailableMemoryMb) | ||
d.Set("region", funcRegion) | ||
timeout, err := readTimeout(getOpt.Timeout) | ||
if err != nil { | ||
return err | ||
} | ||
d.Set("timeout", timeout) | ||
if getOpt.SourceArchiveUrl != "" { | ||
sourceArr := strings.Split(getOpt.SourceArchiveUrl, "/") | ||
d.Set("storage_bucket", sourceArr[2]) | ||
d.Set("storage_object", sourceArr[3]) | ||
} | ||
|
||
if getOpt.HttpsTrigger != nil { | ||
d.Set("trigger_http", true) | ||
} | ||
if getOpt.EventTrigger != nil { | ||
switch getOpt.EventTrigger.EventType { | ||
//From https://github.com/google/google-api-go-client/blob/master/cloudfunctions/v1/cloudfunctions-gen.go#L335 | ||
case "providers/cloud.pubsub/eventTypes/topic.publish": | ||
d.Set("trigger_topic", extractLastResourceFromUri(getOpt.EventTrigger.Resource)) | ||
case "providers/cloud.storage/eventTypes/object.change": | ||
d.Set("trigger_bucket", extractLastResourceFromUri(getOpt.EventTrigger.Resource)) | ||
} | ||
} | ||
d.Set("project", funcProject) | ||
|
||
//Name of function should be unique | ||
d.SetId(d.Get("name").(string)) | ||
|
||
return nil | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd highly recommend taking a look at how we do this sort of thing for other resources- resource_kms_key_ring.go would be a good place to start.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed this file