Add startup-script while creating instance in gcp using golang - go

I am trying to create an instance with a startup-script in gcp using google.golang.org/api/compute/v1. However I am having some problems setting up the metadata to pass the startup-script.
Link to a similar example.
Link to do library documentation.
The function I created is the following one:
func CreateInstance(service *compute.Service, projectId string, instanceName string, zone string) {
imageURL := "https://www.googleapis.com/compute/v1/projects/debian-cloud/global/images/debian-7-wheezy-v20140606"
prefix := "https://www.googleapis.com/compute/v1/projects/" + projectId
file, err := os.Open("startup-script.sh")
if err != nil {
log.Fatal(err)
}
instance := &compute.Instance{
Name: instanceName,
Description: "compute sample instance",
MachineType: prefix + "/zones/" + zone + "/machineTypes/n1-standard-1",
Disks: []*compute.AttachedDisk{
{
AutoDelete: true,
Boot: true,
Type: "PERSISTENT",
InitializeParams: &compute.AttachedDiskInitializeParams{
DiskName: "my-root-pd",
SourceImage: imageURL,
},
},
},
ServiceAccounts: []*compute.ServiceAccount{
{
Email: "default",
Scopes: []string{
compute.DevstorageFullControlScope,
compute.ComputeScope,
},
},
},
Metadata: &compute.Metadata{
{
Items: &compute.MetadataItems{
{
Key: "startup-script",
Value : file,
},
},
},
},
}
op, err := service.Instances.Insert(projectId, zone, instance).Do()
log.Printf("Got compute.Operation, err: %#v, %v", op, err)
etag := op.Header.Get("Etag")
log.Printf("Etag=%v", etag)
}
However I am getting the following error:
./createInstance.go:54:4: missing type in composite literal
./createInstance.go:54:4: too few values in &compute.Metadata literal
Can someone point what I am doing wrong?

The problem are the brackets around Metadata. It should be:
Metadata: &compute.Metadata{
Items: &compute.MetadataItems{
{
Key: "startup-script",
Value : file,
},
},
},

Related

Can not create kubernetes secret using go client

I am trying to create a secret using this function
func (ks *KubeSession) DeploySecret(secretName string, secretType string, secretMap map[string][]byte) error {
InfoLogger.Printf("deploying secret %s", secretName)
secret := corev1.Secret{
TypeMeta: metav1.TypeMeta{
Kind: "Secret",
APIVersion: "v1",
},
ObjectMeta: metav1.ObjectMeta{
Name: secretName,
},
Data: secretMap,
Type: secretType,
}
_, err := ks.ClientSet.CoreV1().Secrets(JOB_NAMESPACE).Create(context.TODO(), &secret, metav1.CreateOptions{})
if err != nil {
return err
}
return nil
}
It is getting the following error:
cannot use secretType (variable of type string) as "k8s.io/api/core/v1".SecretType value in struct literal
Though, it works fine when I use a hardcoded string instead, like that:
...
Type: "generic"
...
How can I substitute this variable, so that I do not have to use hardcoded values instead?
Resolved.
Found the right way to declare the composite type:
Type: corev1.SecretType(secretType)

Go AWS SES Unable to find credentials

I'm trying to send mail using aws sdk v2 for Go.
I'm getting below error. When using s3 client everything is working fine. I checked the permissions associated with the credentials and it has administrator access. Unable to understand what could be the problem.
operation error SES: SendEmail, failed to sign request: failed to retrieve credentials: request canceled, context canceled
config.go
AWSConfig, err = awsConfig.LoadDefaultConfig(context.TODO())
if err != nil {
log.Println("Error configuring aws: ", err)
}
mailer.go
type Mail struct {
To []string
From string
Subject string
Body string
}
func (m *Mail) Send(ctx context.Context) error {
sesClient := ses.NewFromConfig(config.AWSConfig)
result, err := sesClient.SendEmail(ctx, &ses.SendEmailInput{
Destination: &types.Destination{
ToAddresses: m.To,
},
Message: &types.Message{
Body: &types.Body{
Html: &types.Content{
Data: &m.Body,
Charset: &CharSet,
},
},
Subject: &types.Content{
Data: &m.Subject,
Charset: &CharSet,
},
},
Source: &m.From,
ReplyToAddresses: ReplyTo,
ReturnPath: &BounceEmail,
})
if err != nil {
log.Println(fmt.Errorf("[MailSenderUtil]: error sending mail: %w", err))
return err
}
log.Println("[MailSenderUtilResult]: ", InJson(result))
return nil
}
I think you need to add your aws credentials to config.AWSConfig. This is my config
sess, err := session.NewSession(&aws.Config{
Credentials: credentials.NewStaticCredentials(constants.AWSAccessID, constants.AWSAccessKey , ""),
Region:aws.String("eu-west-2")},
)
svc := ses.New(sess)
input := &ses.SendEmailInput{
Destination: &ses.Destination{
ToAddresses: []*string{
aws.String(Recipient),
},
},
Message: &ses.Message{
Body: &ses.Body{
Html: &ses.Content{
Charset: aws.String(CharSet),
Data: aws.String(HtmlBody),
},
Text: &ses.Content{
Charset: aws.String(CharSet),
Data: aws.String(TextBody),
},
},
Subject: &ses.Content{
Charset: aws.String(CharSet),
Data: aws.String(Subject),
},
},
Source: aws.String(Sender),
// Uncomment to use a configuration set
//ConfigurationSetName: aws.String(ConfigurationSet),
}
// Attempt to send the email.
_, err = svc.SendEmail(input)
if err != nil {
if aerr, ok := err.(awserr.Error); ok {
switch aerr.Code() {
case ses.ErrCodeMessageRejected:
logger.Logger.Error(ses.ErrCodeMessageRejected, aerr.OrigErr())
case ses.ErrCodeMailFromDomainNotVerifiedException:
logger.Logger.Error(ses.ErrCodeMailFromDomainNotVerifiedException, aerr.OrigErr())
case ses.ErrCodeConfigurationSetDoesNotExistException:
logger.Logger.Error(ses.ErrCodeConfigurationSetDoesNotExistException, aerr.OrigErr())
default:
logger.Logger.Error("amazon default error", aerr.OrigErr())
}
} else {
logger.Logger.Error("amazon default error (else)", aerr.OrigErr())
}
return
}

Delete DynamoDB string attribute with golang expression package

I'm having trouble deleting an attribute of a dynamodb item using the UpdateItem command: https://docs.aws.amazon.com/sdk-for-go/api/service/dynamodb/expression/#Builder.WithUpdate
expr := expression.Set(
expression.Name("my_attribute_a"),
expression.Value(""),
).Set(
expression.Name("my_attribute_b"),
expression.Value(""),
)
update, err := expression.NewBuilder().
WithUpdate(expr).
Build()
if err != nil {
return fmt.Errorf("failed to build update expression: %v", err)
}
input := &dynamodb.UpdateItemInput{
TableName: &ddb.emailAccountTableName,
ExpressionAttributeNames: update.Names(),
ExpressionAttributeValues: update.Values(),
Key: map[string]*dynamodb.AttributeValue{
"id": {
S: &id,
},
},
UpdateExpression: update.Update(),
ReturnConsumedCapacity: aws.String(dynamodb.ReturnConsumedCapacityTotal),
}
fmt.Println(input.String())
result, err := ddb.svc.UpdateItem(input)
The result of running the above is:
{
ExpressionAttributeNames: {
#1: "my_attribute_a",
#0: "my_attribute_b"
},
ExpressionAttributeValues: {
:0: {
NULL: true
},
:1: {
NULL: true
}
},
Key: {
id: {
S: "38zqtaNezbB8eZw4pbJKm7"
}
},
ReturnConsumedCapacity: "TOTAL",
TableName: "my-table",
UpdateExpression: "SET #0 = :0, #1 = :1\n"
}
And the result is the attributes for the item get set to true. I want the attributes to be removed. I've tried using nil but that has the same result, and expression.Null just sets the string to the value NULL
Turns out I needed to use expression.Remove instead of expression.Set here's the relevant update:
expr := expression.Remove(
expression.Name("my_attribute_a"),
).Remove(
expression.Name("my_attribute_b"),
)
update, err := expression.NewBuilder().
WithUpdate(expr).
Build()
Which results in the following:
{
ExpressionAttributeNames: {
#0: "my_attribute_a",
#1: "my_attribute_b
},
Key: {
id: {
S: "qfsphqt37ELyn2BanwE2fd"
}
},
ReturnConsumedCapacity: "TOTAL",
TableName: "my-table",
UpdateExpression: "REMOVE #0, #1\n"
}

What is the proper way to save a slice of structs into Cloud Datastore (Firestore in Datastore Mode)?

I want to save a slice of structs in Google Cloud Datastore (Firestore in Datastore mode).
Take this Phonebook and Contact for example.
type Contact struct {
Key *datastore.Key `json:"id" datastore:"__key__"`
Email string `json:"email" datastore:",noindex"`
Name string `json:"name" datastore:",noindex"`
}
type Phonebook struct {
Contacts []Contact
Title string
}
Saving and loading this struct is no problem as the Datastore library takes care of it.
Due to the presence of some complex properties in my actual code, I need to implement PropertyLoadSaver methods.
Saving the Title property is straightforward. But I have problems storing the slice of Contact structs.
I tried using the SaveStruct method:
func (pb *Phonebook) Save() ([]datastore.Property, error) {
ps := []datastore.Property{
{
Name: "Title",
Value: pb.Title,
NoIndex: true,
},
}
ctt, err := datastore.SaveStruct(pb.Contacts)
if err != nil {
return nil, err
}
ps = append(ps, datastore.Property{
Name: "Contacts",
Value: ctt,
NoIndex: true,
})
return ps, nil
}
This code compiles but doesn't work.
The error message is datastore: invalid entity type
Making a slice of Property explicitly also does not work:
func (pb *Phonebook) Save() ([]datastore.Property, error) {
ps := []datastore.Property{
{
Name: "Title",
Value: pb.Title,
NoIndex: true,
},
}
cttProps := datastore.Property{
Name: "Contacts",
NoIndex: true,
}
if len(pb.Contacts) > 0 {
props := make([]interface{}, 0, len(pb.Contacts))
for _, contact := range pb.Contacts {
ctt, err := datastore.SaveStruct(contact)
if err != nil {
return nil, err
}
props = append(props, ctt)
}
cttProps.Value = props
}
ps = append(ps, cttProps)
return ps, nil
}
Making a slice of Entity does not work either:
func (pb *Phonebook) Save() ([]datastore.Property, error) {
ps := []datastore.Property{
{
Name: "Title",
Value: pb.Title,
NoIndex: true,
},
}
cttProps := datastore.Property{
Name: "Contacts",
NoIndex: true,
}
if len(pb.Contacts) > 0 {
values := make([]datastore.Entity, len(pb.Contacts))
props := make([]interface{}, 0, len(pb.Contacts))
for _, contact := range pb.Contacts {
ctt, err := datastore.SaveStruct(contact)
if err != nil {
return nil, err
}
values = append(values, datastore.Entity{
Properties: ctt,
})
}
for _, v := range values {
props = append(props, v)
}
cttProps.Value = props
}
ps = append(ps, cttProps)
return ps, nil
}
Both yielded the same error datastore: invalid entity type
Finally I resorted to using JSON. The slice of Contact is converted into a JSON array.
func (pb *Phonebook) Save() ([]datastore.Property, error) {
ps := []datastore.Property{
{
Name: "Title",
Value: pb.Title,
NoIndex: true,
},
}
var values []byte
if len(pb.Contacts) > 0 {
js, err := json.Marshal(pb.Contacts)
if err != nil {
return nil, err
}
values = js
}
ps = append(ps, datastore.Property{
Name: "Contacts",
Value: values,
NoIndex: true,
})
return ps, nil
}
Isn't there a better way of doing this other than using JSON?
I found this document and it mentions src must be a struct pointer.
The only reason you seem to customize the saving of PhoneBook seems to be to avoid saving the Contacts slice if there are no contacts. If so, you can just define your PhoneBook as follows and directly use SaveStruct on the PhoneBook object.
type Phonebook struct {
Contacts []Contact `datastore:"Contacts,noindex,omitempty"`
Title string `datastore:"Title,noindex"`
}

Dynamic group names with viper

I'm trying to get some groups from the config, which names are not known (will get more later on).
I'm new to golang and struggling a bit. I'm using Viper, because of its support for yaml, json and toml.
json config:
{
"database": {
"db": "mydb",
"host": "localhost",
"pass": "mypassword",
"port": 3306,
"user": "username"
},
"customers": {
"company_one": {
"address": "66 Great Queen St, London WC2B 5BX, UK",
"contacts": [
{
"email": "joe.doe#company-one.local",
"name": "Joe Doe"
},
{
"email": "jane.doe#company-one.local",
"name": "Jane Doe"
}
]
},
"company_two": {
"address": "Irish Town Pl, Gibraltar GX11 1AA, Gibraltar",
"contacts": [
{
"email": "lucky.luke#second-company.local",
"name": "Lucky Luke"
}
]
}
}
}
and the source:
package main
import (
"fmt"
"log"
"github.com/spf13/viper"
)
type Config struct {
database Database
customers Customer
}
type Database struct {
host string
port uint16
user string
pass string
db string
}
type Customer struct {
company Company
}
type Company struct {
contacts Contact
address string
}
type Contact struct {
name string
email string
}
func main() {
viper.SetConfigName("a")
viper.AddConfigPath(".")
if err := viper.ReadInConfig(); err != nil {
log.Fatal("Unable to read config file", err)
}
var conf Config
database := viper.GetStringMap("database")
for key, i := range database {
switch key {
case "host":
conf.database.host = i.(string)
case "port":
conf.database.port = i.(uint16)
case "user":
conf.database.user = i.(string)
case "pass":
conf.database.pass = i.(string)
case "db":
conf.database.db = i.(string)
}
}
fmt.Printf("%v\n", conf)
}
I have no idea how to cycle through the customers.
I have tried this one already (converted from go-toml to viper), but it didn't work as expected:
var contMap = map[string]Contacts
cust := config.Get("customers")
for _, c := range cust.Keys() {
sub := cust.Get(c).([]interface{})
for _,d := range sub{
address := d.([]interface{})[0].(string)
hostMap[host] = Contacts{
email: email,
name: name,
}
}
log.Printf("Customers: %s contact: %q", c, sub)
}
I started down the road of answering your initial question but it is so cumbersome and probably not what you actually want.
Your code as you've pasted it has a bunch of errors in it so instead let me offer a simpler solution like I was talking about in my comment.
package main
import (
"log"
"github.com/spf13/viper"
)
type Config struct {
Database Database `mapstructure:"database"`
Customers map[string]Company `mapstructure:"customers"`
}
type Database struct {
Host string `mapstructure:"host"`
Port uint16 `mapstructure:"port"`
User string `mapstructure:"user"`
Pass string `mapstructure:"pass"`
Db string `mapstructure:"db"`
}
type Company struct {
Address string `mapstructure:"address"`
Contacts []Contact `mapstructure:"contacts"`
}
type Contact struct {
Name string `mapstructure:"name"`
Email string `mapstructure:"email"`
}
func main() {
viper.SetConfigName("config")
viper.AddConfigPath(".")
if err := viper.ReadInConfig(); err != nil {
log.Fatal("Unable to read config file", err)
}
var conf Config
err := viper.Unmarshal(&conf)
if err != nil {
panic(err)
}
log.Printf("%#v", conf)
}
If you run this code with your JSON config file (named config) it returns the following output:
2018/04/28 14:47:54
main.Config{Database:main.Database{Host:"localhost", Port:0xcea,
User:"username", Pass:"mypassword", Db:"mydb"},
Customers:map[string]main.Company{"company_two":main.Company{Address:"Irish
Town Pl, Gibraltar GX11 1AA, Gibraltar",
Contacts:[]main.Contact{main.Contact{Name:"Lucky Luke",
Email:"lucky.luke#second-company.local"}}},
"company_one":main.Company{Address:"66 Great Queen St, London WC2B
5BX, UK", Contacts:[]main.Contact{main.Contact{Name:"Joe Doe",
Email:"joe.doe#company-one.local"}, main.Contact{Name:"Jane Doe",
Email:"jane.doe#company-one.local"}}}}}
And here it is reformatted as it would be written if you were creating this whole structure in code:
Config{
Database{
Host: "localhost",
Port: 0xcea,
User: "username",
Pass: "mypassword",
Db: "mydb"},
Customers: map[string]Company{
"company_two": Company{Address: "Irish Town Pl, Gibraltar GX11 1AA, Gibraltar",
Contacts: []Contact{
Contact{Name: "Lucky Luke", Email: "lucky.luke#second-company.local"}}},
"company_one": Company{Address: "66 Great Queen St, London WC2B 5BX, UK",
Contacts: []Contact{
Contact{Name: "Joe Doe", Email: "joe.doe#company-one.local"},
Contact{Name: "Jane Doe", Email: "jane.doe#company-one.local"}}},
},
}

Resources