DevTips -- Go

moon indicating dark mode
sun indicating light mode
Go is an open source programming language that makes it easy to build simple, reliable, and efficient software.

dump env vars

Sometimes you need to brute force it. To dump all environment variables from inside a @golang program, we can combine `range` and `os.Environ`

package main
import (
"os"
"fmt"
)
func main() {
for _, pair := range os.Environ() {
fmt.Println(pair)
}
}
LANG=en_US.UTF-8
PWD=/Users/christopherbiscardi/tmp
SHELL=/bin/zsh
TERM_PROGRAM_VERSION=3.2.9
TERM_PROGRAM=iTerm.app
COLORTERM=truecolor
COMMAND_MODE=unix2003
TERM=xterm-256color
HOME=/Users/christopherbiscardi
TMPDIR=/var/folders/br/5cvw25pn6zjbn5nqhf0ptnx80000gn/T/
PAGER=less
LESS=-R

wrapping lambdas with context

Using @golang modules you can easily create packages that wrap functionality for @netlify lambdas, making it easier to focus on business logic and create more functions

package netlify
import (
"context"
"os"
"time"
"github.com/aws/aws-lambda-go/events"
"github.com/aws/aws-lambda-go/lambda"
"github.com/aws/aws-lambda-go/lambdacontext"
"github.com/honeycombio/libhoney-go"
"github.com/honeycombio/libhoney-go/transmission"
)
type lambdaFunc = func(context.Context, events.APIGatewayProxyRequest) (*events.APIGatewayProxyResponse, error)
type UserLambdaFunc = func(*libhoney.Event, events.APIGatewayProxyRequest) (*events.APIGatewayProxyResponse, error)
func handler(userHandleFunc UserLambdaFunc) lambdaFunc {
return func(ctx context.Context, request events.APIGatewayProxyRequest) (*events.APIGatewayProxyResponse, error) {
// This event will be sent regardless of how we exit
defer libhoney.Flush()
// start the timer to see how long lambda runs
startTime := time.Now()
// aws contexts have some juicy information to capture
lc, ok := lambdacontext.FromContext(ctx)
if !ok {
return &events.APIGatewayProxyResponse{
StatusCode: 503,
Body: "Something went wrong :(",
}, nil
}
cc := lc.ClientContext
// Create an event, add some data
ev := libhoney.NewEvent()
ev.Add(map[string]interface{}{
"method": request.HTTPMethod,
"request_id": request.RequestContext.RequestID,
"request_path": request.Path,
"function_name": lambdacontext.FunctionName,
"function_version": lambdacontext.FunctionVersion,
"memory_limit_in_mb": lambdacontext.MemoryLimitInMB,
"aws_request_id": lc.AwsRequestID,
"invoked_function_arn": lc.InvokedFunctionArn,
"client_installation_id": cc.Client.InstallationID,
"client_app_title": cc.Client.AppTitle,
"client_app_version_code": cc.Client.AppVersionCode,
"client_app_package_name": cc.Client.AppPackageName,
})
// pass control to the user
response, err := userHandleFunc(ev, request)
// add the final runtime of the function
ev.Add(map[string]interface{}{
"duration_ns": time.Since(startTime),
})
// send event, if we don't do this libhoney tries to batch,
// which doesn't work in lambdas because there is only one
// execution before sleep
ev.Send()
// return the result of the user's handler to aws's handling
return response, err
}
}
func BootstrapMain(userHandler UserLambdaFunc) {
writeKey, exists := os.LookupEnv("HONEYCOMB_WRITE_KEY")
// exists is true if the env exists, but can be empty
if exists && writeKey != "" {
libhoney.Init(libhoney.Config{
WriteKey: writeKey,
Dataset: "netlify-serverless",
})
} else {
libhoney.Init(libhoney.Config{
Dataset: "netlify-serverless",
Transmission: &transmission.WriterSender{},
})
}
// Flush any pending calls to Honeycomb before exiting
defer libhoney.Close()
// Make the handler available for Remote Procedure Call by AWS Lambda
lambda.Start(handler(userHandler))
}
package main
import (
"github.com/aws/aws-lambda-go/events"
"github.com/christopherbiscardi/sector/go-src/netlify"
"github.com/honeycombio/libhoney-go"
)
func handler(ev *libhoney.Event, request events.APIGatewayProxyRequest) (*events.APIGatewayProxyResponse, error) {
ev.AddField("hit", "the test function")
return &events.APIGatewayProxyResponse{
StatusCode: 200,
Body: "Hello, World",
}, nil
}
func main() {
netlify.BootstrapMain(handler)
}

range

\@golang doesn't have `.map`, but it does have for loop sugar called `range` which can be used to iterate over collections.

var nums = []int{10, 20, 30, 40, 50, 60, 70}
func main() {
for i, v := range nums {
fmt.Println(v)
}
}

Viper isSet

Viper (https://github.com/spf13/viper) lets you check to see if environment variables are set, so you can use that to early terminate a @golang program

for _, env := range []string{
"TWITTER_ACCESS_TOKEN",
"TWITTER_ACCESS_TOKEN_SECRET",
"TWITTER_CONSUMER_KEY",
"TWITTER_CONSUMER_SECRET",
}{
viper.BindEnv(env)
isSet := viper.IsSet(env)
if isSet == false {
panic(fmt.Sprintf("%v is not set", env))
}
}
access_token := viper.GetString("TWITTER_ACCESS_TOKEN")
access_token_secret := viper.GetString("TWITTER_ACCESS_TOKEN_SECRET")
consumer_key := viper.GetString("TWITTER_CONSUMER_KEY")
consumer_secret := viper.GetString("TWITTER_CONSUMER_SECRET")