How to pass variadic functions into another function - go

i am working with aws-sdk-v2 and I want to make a minimum working example using "secretsmanager" service.
I am trying to follow the steps in this similiar example which is using "kms" service.
here is my script:
package main
import (
"context"
"fmt"
"log"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/feature/ec2/imds"
"github.com/aws/aws-sdk-go-v2/service/secretsmanager"
)
func main() {
cfg, err := config.LoadDefaultConfig(context.TODO())
if err != nil {
log.Printf("error: %v", err)
return
}
client := imds.NewFromConfig(cfg)
region, err := client.GetRegion(context.TODO(), &imds.GetRegionInput{})
if err != nil {
log.Printf("Unable to retrieve the region from the EC2 instance %v\n", err)
}
fmt.Printf(region.Region)
svc := secretsmanager.NewFromConfig(cfg)
input := &secretsmanager.CreateSecretInput{Name: aws.String("test")}
opts := &secretsmanager.Options{Region: region.Region}
result, err := svc.CreateSecret(context.TODO(), input, opts)
if err != nil {
fmt.Println(err)
}
fmt.Println(result)
}
error:
./main.go:38:46: cannot use opts (type *secretsmanager.Options) as type func(*secretsmanager.Options) in argument to svc.CreateSecret
so the error is obviously in those line:
opts := &secretsmanager.Options{Region: region.Region}
result, err := svc.CreateSecret(context.TODO(), input, opts)
from the documentation, the function CreateSecret takes these input types:
func (c *Client) CreateSecret(ctx context.Context, params *CreateSecretInput, optFns ...func(*Options)) (*CreateSecretOutput, error)
I can't find out how can I create this ...func(*Options) part in my context. Can someone please help me with this part?

I figured it out:
opts := func(o *secretsmanager.Options) {
o.Region = region.Region
}
result, err := svc.CreateSecret(context.TODO(), input, opts)

Related

How to define a connect to DB function in golang using gorm

Good morning,
I am new to golang. I am creating following connect to DB function:
func ConnectDatabase(db gorm.Dialector, gorm.Config gorm.Option) *gorm.DB{
db, err := gorm.Open(db, gormConfig)
if err != nil {
log.Fatal(err)
}
return db
}
It gives me an error on Open: cannot use gorm.Open(db, gormConfig)(value of type *gorm.DB) as gorm.Dialector value in assignment: *gorm.DB does not implement gorm.Dialector
The reason I create the function with two parameteres is because i want to wright the test on it, and need to pass test parameters to it
My test code looks like it:
func TestDbConnect(t *testing.T){
dbFile, err := os.CreateTemp("", "sample_db"
if err != nil{
t.Fatal(err)
}
sqlOpen := sqlite.Open(dbFile.Name())
gormConfig := &gorm.Config{}
conn, err := sqlOpen, gormConfig
}
Would really appreciate an advice on how to define my function correctly and if my test function makes sense
You have some improper function signature ,in parameters .I have fixed your code .Now you can use it
package main
import (
"fmt"
"log"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
)
func ConnectDatabase(db gorm.Dialector, config *gorm.Config, option gorm.Option) (*gorm.DB,err) {
db1, err := gorm.Open(db, config)
if err != nil {
return nil,err // return nil and error here
}
return db1,nil
}
func main() {
sql := sqlite.Open("test.db")
db := ConnectDatabase(sql, &gorm.Config{}, nil)
fmt.Println("Hello, World!", db)
}
Points -
1- Dialector is an interface of gorm
2- sqlite.Open("test.db") returns Dialector interface
Your test would look something like this
func TestDbConnect(t *testing.T){
dbFile, err := os.CreateTemp("", "sample_db"
if err != nil{
t.Fatal(err)
}
sqlOpen := sqlite.Open(dbFile.Name())
db,err := ConnectDatabase(sqlOpen, &gorm.Config{}, nil)
// handle error and test pass case
}
Also don't make function local variable name as parameter name,this may cause variable shadowing see here as you are having err variable as new one in this line
db, err := gorm.Open(db, gormConfig)
so go complier was not giving error.

GCP - get project NAT GW's

We have account on GCP which contain valid cloud Nat, now we want to get those values via
GCP sdk, I've tried the following and get empty response (maybe I use the wrong API and it not ListExternalVpnGatewaysRequest)
package main
import (
"context"
"fmt"
compute "cloud.google.com/go/compute/apiv1"
"google.golang.org/api/iterator"
computepb "google.golang.org/genproto/googleapis/cloud/compute/v1"
)
func main() {
ctx := context.Background()
c, err := compute.NewExternalVpnGatewaysRESTClient(ctx)
if err != nil {
fmt.Println(err)
}
defer c.Close()
proj := "dev-proj"
req := &computepb.ListExternalVpnGatewaysRequest{
//Filter: new(string),
//MaxResults: new(uint32),
//OrderBy: new(string),
//PageToken: new(string),
Project: proj,
//ReturnPartialSuccess: new(bool),
}
it := c.List(ctx, req)
for {
resp, err := it.Next()
if err == iterator.Done {
break
}
if err != nil {
fmt.Println(err)
}
// TODO: Use resp.
_ = resp
fmt.Println(resp)
}
}
I need to get the following values using GCP GO SDK
update
I tried the following as-is and I got error
package main
import (
"context"
"fmt"
"google.golang.org/api/compute/v1"
"log"
)
func main() {
project := "my-proj"
region := "my-region"
ctx := context.Background()
computeService, err := compute.New(ctx)
if err != nil {
log.Fatal(err)
}
req := computeService.Routers.List(project, region)
if err := req.Pages(ctx, func(page *compute.RouterList) error {
for _, router := range page.Items {
// process each `router` resource:
fmt.Printf("%#v\n", router)
// NAT Gateways are found in router.nats
}
return nil
}); err != nil {
log.Fatal(err)
}
}
Error is: ./main.go:16:36: cannot use ctx (type context.Context) as type *http.Client in argument to compute.New
A VPN Gateway is not the same as a NAT Gateway.
Use this code to list routers. Within the list of routers, is the NAT Gateways
import "google.golang.org/api/compute/v1"
// Replace with valid values for your project
project := "my-project"
region := "my-region"
ctx := context.Background()
c, err := google.DefaultClient(ctx, compute.CloudPlatformScope)
if err != nil {
log.Fatal(err)
}
computeService, err := compute.New(c)
if err != nil {
log.Fatal(err)
}
req := computeService.Routers.List(project, region)
if err := req.Pages(ctx, func(page *compute.RouterList) error {
for _, router := range page.Items {
// process each `router` resource:
fmt.Printf("%#v\n", router)
// NAT Gateways are found in router.nats
}
return nil
}); err != nil {
log.Fatal(err)
}
SDK Documentation

How can I use the AWS SDK v2 for Go with DigitalOcean Spaces?

I'm trying to use the AWS v2 SDK for Go to list all objects in a given bucket on DigitalOcean Spaces. Their documentation gives examples of how to use the v1 SDK to do this, but my app uses v2. I know I could technically use both, but I'd rather not if possible.
Here's what I've got so far:
package main
import (
"context"
"fmt"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/service/s3"
)
func main() {
customResolver := aws.EndpointResolverWithOptionsFunc(func(service, region string, options ...interface{}) (aws.Endpoint, error) {
return aws.Endpoint{
URL: "https://sfo2.digitaloceanspaces.com",
}, nil
})
cfg, err := config.LoadDefaultConfig(
context.TODO(),
config.WithRegion("us-east-1"),
config.WithEndpointResolverWithOptions(customResolver),
config.WithCredentialsProvider(aws.AnonymousCredentials{}),
)
if err != nil {
fmt.Println(err)
}
s3Client := s3.NewFromConfig(cfg)
var continuationToken *string
continuationToken = nil
for {
output, err := s3Client.ListObjectsV2(context.TODO(), &s3.ListObjectsV2Input{
Bucket: aws.String("stats"),
ContinuationToken: continuationToken},
)
if err != nil {
fmt.Println(err)
}
for _, obj := range output.Contents {
fmt.Println(obj)
}
if output.IsTruncated == false {
break
}
continuationToken = output.ContinuationToken
}
}
This is the error I'm getting:
operation error S3: ListObjectsV2, https response error StatusCode: 400, RequestID: tx0000000000000051339d4-00620701db-2174fe1c-sfo2a, HostID: 2174fe1c-sfo2a-sfo, api error InvalidArgument: UnknownError
The error seems to indicate there's something wrong with my request but I don't know what.
For pagination i think you need to do it via a pagination function
like this
// Create the Paginator for the ListObjectsV2 operation.
p := s3.NewListObjectsV2Paginator(client, params, func(o *s3.ListObjectsV2PaginatorOptions) {
if v := int32(maxKeys); v != 0 {
o.Limit = v
}
})
Here's a fully working example I'm using to read from a digital ocean spaces bucket
package s3
import (
"context"
"os"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/credentials"
"github.com/aws/aws-sdk-go-v2/feature/s3/manager"
"github.com/aws/aws-sdk-go-v2/service/s3"
)
func read(ctx context.Context) error {
// Define the parameters for the session you want to create.
spacesKey := os.Getenv("SPACES_KEY")
spacesSecret := os.Getenv("SPACES_SECRET")
creds := credentials.NewStaticCredentialsProvider(spacesKey, spacesSecret, "")
customResolver := aws.EndpointResolverWithOptionsFunc(func(service, region string, options ...interface{}) (aws.Endpoint, error) {
return aws.Endpoint{
URL: "https://sfo3.digitaloceanspaces.com",
}, nil
})
cfg, err := config.LoadDefaultConfig(ctx,
config.WithRegion("us-east-1"),
config.WithCredentialsProvider(creds),
config.WithEndpointResolverWithOptions(customResolver))
if err != nil {
return err
}
// Create an Amazon S3 service client
awsS3Client := s3.NewFromConfig(cfg)
input := &s3.GetObjectInput{
Bucket: aws.String("zeus-fyi"),
Key: aws.String("test.txt"),
}
downloader := manager.NewDownloader(awsS3Client)
newFile, err := os.Create("./local-test.txt")
if err != nil {
return err
}
defer newFile.Close()
_, err = downloader.Download(ctx, newFile, input)
if err != nil {
return err
}
return err
}

Golang: no new variables on left side of :=, while a similar one didn't occur this error

I have written some code to prints the content found at a URL following the guide of a book named The Go Programming Language. The compiler complained about the following code that no new variables on left side of :=.
package main
import (
"fmt"
"net/http"
"os"
"io"
)
func main() {
for _, url := range os.Args[1:] {
resp, err := http.Get(url)
if err != nil {
fmt.Fprintf(os.Stderr, "fetch: %v\n", err)
os.Exit(1)
}
_, err := io.Copy(os.Stdout, resp.Body)
resp.Body.Close()
if err != nil {
fmt.Fprintf(os.Stderr, "fetch: reading %s: %v\n", url, err)
os.Exit(1)
}
}
}
And I have known that this was caused by re-declaration of certain variables. While the following one passed the compilation.
package main
import (
"fmt"
"io/ioutil"
"net/http"
"os"
)
func main() {
for _, url := range os.Args[1:] {
resp, err := http.Get(url)
if err != nil {
fmt.Fprintf(os.Stderr, "fetch: %v\n", err)
os.Exit(1)
}
b, err := ioutil.ReadAll(resp.Body)
resp.Body.Close()
if err != nil {
fmt.Fprintf(os.Stderr, "fetch: reading %s: %v\n", url, err)
os.Exit(1)
}
fmt.Printf("%s", b)
}
}
Didn't it re-declare the variable err? Then how could it pass the compilation?
If there are any new variables in the assignment (e.g. b) then := will create it. If all variables are not new then you get the error you're getting. _ is not a new variable.
Since you have all existing variables you can just use = for your io line:
_, err = io.Copy(os.Stdout, resp.Body)
When you used b instead of _ then := declared b but treated err like a standard assignment (i.e. just as if a = was used)
It did not
You can use the := as long as there is at least one new variable at the left side and the other variables have already been declared in the same block with the same type. Already declared variables will just have their values changed. It's basically for convenience.
It is all described here : https://golang.org/ref/spec#Short_variable_declarations

Go to check EC2 environment? Or DNS domain name?

I want to see if the program is being run in EC2 or not.
One way is to run hostname -d in EC2 to get the DNS domain name.
How do I get this DNS domain name in Go.
I looked at the net package using http://golang.org/pkg/net/#LookupNS
But I need to pass an argument.
How do I check if it's in EC2 or not?
Thanks
You can see if there is an interface with a specific name with this function:
package main
import (
"log"
"net"
"strings"
)
func trace(fmt string, args ...interface{}) {
log.Printf(fmt, args...)
}
func HasAddrWithName(name string) (bool, error) {
ifaces, err := net.Interfaces()
if err != nil {
return false, err
}
for _, iface := range ifaces {
addrs, err := iface.Addrs()
if err != nil {
trace("%s", err)
continue
}
for _, addr := range addrs {
ipaddr, _, err := net.ParseCIDR(addr.String())
if err != nil {
trace("%s", err)
continue
}
hosts, err := net.LookupAddr(ipaddr.String())
if err != nil {
trace("%s", err)
continue
}
for idx, h := range hosts {
trace("%d: %s\n", idx, h)
if strings.Contains(h, name) {
return true, nil
}
}
}
}
return false, nil
}
func main() {
hasAddr, err := HasAddrWithName(".ec2.internal")
if err != nil {
log.Fatal(err)
}
if hasAddr {
log.Println("inside ec2")
return
}
log.Println("Not inside ec2")
}
The function will try to find all the interface an resolve the ip to a dns name. if the name contains the specific string returns true.
The right way, IMO, is to try and hit the metadata API at http://169.254.169.254/latest/meta-data from the machine itself. The worrisome part is that you feel the need to know this in code. I am not quite sure what the use case for this is but it seems to me that there ought to be a way for you to know this outside of your code.
Nevertheless:
package main
import (
"fmt"
"io/ioutil"
"net/http"
"os"
"time"
)
func main() {
/* if you just need the hostname */
name, _ := os.Hostname()
fmt.Println(name)
/* if you must hit the EC2 metadata API */
client := http.Client{
Timeout: time.Duration(2 * time.Second),
}
resp, err := client.Get("http://169.254.169.254/latest/meta-data/public-hostname")
if err != nil {
fmt.Println("Probably not on EC2")
fmt.Println(err)
return
}
defer resp.Body.Close()
body, _ := ioutil.ReadAll(resp.Body)
fmt.Println(string(body))
}

Resources