AWS Golang SDK v2 - How to add function to Go AWS script - go

Trying to seperate out each part of the script into functions to use the output later on.
Cannot get this part to work when trying to pass in instances to the printVolumesInfo function.
[]InstanceBlockDeviceMapping is part of the Instance struct but I am unsure what to use as an input for the function.
`
package main
import (
"context"
"fmt"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/service/ec2"
)
var client *ec2.Client
func init() {
cfg, err := config.LoadDefaultConfig(context.TODO())
if err != nil {
panic("configuration error, " + err.Error())
}
client = ec2.NewFromConfig(cfg)
}
func printVolumesInfo(volumes []ec2.InstanceBlockDeviceMapping) {
for _, b := range volumes {
fmt.Println(" " + *b.DeviceName)
fmt.Println(" " + *b.Ebs.VolumeId)
}
}
func main() {
parms := &ec2.DescribeInstancesInput{}
result, err := client.DescribeInstances(context.TODO(), parms)
if err != nil {
fmt.Println("Error calling ec2: ", err)
return
}
for _, r := range result.Reservations {
fmt.Println("Reservation ID: " + *r.ReservationId)
fmt.Println("Instance IDs:")
for _, i := range r.Instances {
fmt.Println(" " + *i.InstanceId)
printVolumesInfo(i.InstanceBlockDeviceMapping)
}
}
}
`
Error received:
./main.go:74:37: undefined: ec2.InstanceBlockDeviceMapping
Tried to use different parameters including []InstanceBlockDeviceMapping and BlockDeviceMapping. Also, used ec2 and client for the values as well.

Check the documentation: https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/ec2/types#Instance
The field is called BlockDeviceMappings. And the type InstanceBlockDeviceMapping is in the package github.com/aws/aws-sdk-go-v2/service/ec2/types, not in the ec2 package.
Add github.com/aws/aws-sdk-go-v2/service/ec2/types` to your imports
Change argument type of function printVolumes to volumes []ec2.InstanceBlockDeviceMapping
Call the function as printVolumesInfo(i.BlockDeviceMappings)

Related

Running Azure SDK for Go return an error: MSI not available

I am trying to run Golang Azure SDK code to get a list of RGs in my subscriptions but I am getting the following error:
2022/01/22 20:25:58 MSI not available
exit status 1
import (
"context"
"fmt"
"github.com/Azure/azure-sdk-for-go/services/resources/mgmt/2020-10-01/resources"
"github.com/Azure/go-autorest/autorest/azure/auth"
"github.com/Azure/go-autorest/autorest/to"
"log"
"os"
)
func main() {
authorize, err := auth.NewAuthorizerFromEnvironment()
if err != nil {
log.Fatal(err)
}
subscriptionID := os.Getenv("AZURE_SUB_ID")
//Read resource groups
resGrpClient := resources.NewGroupsClient(subscriptionID)
resGrpClient.Authorizer = authorize
//Read resources within the resource group
resClient := resources.NewClient(subscriptionID)
resClient.Authorizer = authorize
for resGrpPage, err := resGrpClient.List(context.Background(), "", nil); resGrpPage.NotDone(); err = resGrpPage.Next() {
if err != nil {
log.Fatal(err)
}
for _, resGrp := range resGrpPage.Values() {
fmt.Println("Resource Group Name: ", to.String(resGrp.Name))
resList, _ := resClient.ListByResourceGroup(context.Background(), to.String(resGrp.Name), "", "", nil)
for _, res := range resList.Values() {
fmt.Println("\t- Resource Name: ", to.String(res.Name), " | Resource Type: ", to.String(res.Type))
}
}
}
}
I am using Goland and trying to run the app in WSL Ubuntu
Solution is to use auth.NewAuthorizerFromCLI(), as auth.NewAuthorizerFromEnvironment does not use the Cli and MSI stands for managed system identity.
Please read this documentation Use environment-based authentication
You have a couple of options
and they need specific environments variables to be present.
In my case, I use Client credentials so I need to have these 3 envs present when I run my code.
AZURE_CLIENT_ID
AZURE_CLIENT_SECRET
AZURE_TENANT_ID

How to validate API key with function in Martini?

So I currently have a function that will take in a string APIKey to check it against my Mongo collection. If nothing is found (not authenticated), it returns false - if a user is found, it returns true. My problem, however, is I'm unsure how to integrate this with a Martini POST route. Here is my code:
package main
import (
"context"
"fmt"
"log"
"os"
"github.com/go-martini/martini"
_ "github.com/joho/godotenv/autoload"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)
type User struct {
Name string
APIKey string
}
func validateAPIKey(users *mongo.Collection, APIKey string) bool {
var user User
filter := bson.D{{"APIKey", APIKey}}
if err := users.FindOne(context.TODO(), filter).Decode(&user); err != nil {
fmt.Printf("Found 0 results for API Key: %s\n", APIKey)
return false
}
fmt.Printf("Found: %s\n", user.Name)
return true
}
func uploadHandler() {
}
func main() {
mongoURI := os.Getenv("MONGO_URI")
mongoOptions := options.Client().ApplyURI(mongoURI)
client, _ := mongo.Connect(context.TODO(), mongoOptions)
defer client.Disconnect(context.TODO())
if err := client.Ping(context.TODO(), nil); err != nil {
log.Fatal(err, "Unable to access MongoDB server, exiting...")
}
// users := client.Database("sharex_api").Collection("authorized_users") // commented out when testing to ignore unused warnings
m := martini.Classic()
m.Post("/api/v1/upload", uploadHandler)
m.RunOnAddr(":8085")
}
The validateAPIKey function works exactly as intended if tested alone, I am just unsure how I would run this function for a specific endpoint (in this case, /api/v1/upload).

Mismatch of types between actual function and test function

I have written the following function. Its does the following
Traverse through a given namespace and iterate through all pods.
Return a string slice containing pods that match a naming convention.
var slavePods []string
func getSlavePods(clientset *kubernetes.Clientset, ns string, release string) []string {
log.Printf("INFO :: Checking slave pods in %s namespace.\n", ns)
pods, err := clientset.CoreV1().Pods(ns).List(context.TODO(), metav1.ListOptions{})
if err != nil {
panic(err.Error())
}
for _, po := range pods.Items {
SlavePodRegex := "zjs-" + release + "-(tiny|small|medium|large)-(.*)"
log.Printf("INFO :: Regex is %s", SlavePodRegex)
matched, _ := regexp.MatchString(SlavePodRegex , po.Name)
if matched{
slavePods = append(slavePods, po.Name)
}
}
return slavePods
}
In the above case, the clientset is of type *"k8s.io/client-go/kubernetes".Clientset
The above functions works well and returns the required result.
Now I am writing a test for this function. The test is
import (
"context"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes/fake"
"log"
"testing"
)
func TestgetSlavePods(t *testing.T) {
// Create the fake client.
client := fake.NewSimpleClientset()
// Inject an event into the fake client.
p := &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "zjs-modelt-develop-small-n3d8t"}}
_, err := client.CoreV1().Pods("test-ns").Create(context.TODO(), p, metav1.CreateOptions{})
if err != nil {
t.Fatalf("error injecting pod add: %v", err)
}
// Actual test
podNames := getSlavePods(client, "test-ns", "modelt-develop")
if podNames != ["zjs-modelt-develop-small-n3d8t"] {
t.Error("Expected pod is not found")
}
}
I am leveraging the fake library to initialize a fake client, create a pod in a namespace and then test the function. But I am getting an error
Cannot use 'client' (type *"k8s.io/client-go/kubernetes/fake".Clientset) as type *"k8s.io/client-go/kubernetes".Clientset
So seems like there is an issue where I cannot invoke the function with the clientset because the types are different in the actual function v/s the test function. I am following this example and they seem to be doing the samething. Is there something I have missed ?

Golang patterns for stdin testing

EDIT: Adrian's suggestion makes sense, so I moved my code into a function and called the function from my cobra block:
package cmd
import (
"fmt"
"log"
"os"
"io"
"github.com/spf13/cobra"
"github.com/spf13/viper"
input "github.com/tcnksm/go-input"
)
var configureCmd = &cobra.Command{
Use: "configure",
Short: "Configure your TFE credentials",
Long: `Prompts for your TFE API credentials, then writes them to
a configuration file (defaults to ~/.tgc.yaml`,
Run: func(cmd *cobra.Command, args []string) {
CreateConfigFileFromPrompts(os.Stdin, os.Stdout)
},
}
func CreateConfigFileFromPrompts(stdin io.Reader, stdout io.Writer) {
ui := &input.UI{
Writer: stdout,
Reader: stdin,
}
tfeURL, err := ui.Ask("TFE URL:", &input.Options{
Default: "https://app.terraform.io",
Required: true,
Loop: true,
})
if err != nil {
log.Fatal(err)
}
viper.Set("tfe_url", tfeURL)
tfeAPIToken, err := ui.Ask(fmt.Sprintf("TFE API Token (Create one at %s/app/settings/tokens)", tfeURL), &input.Options{
Default: "",
Required: true,
Loop: true,
Mask: true,
MaskDefault: true,
})
if err != nil {
log.Fatal(err)
}
viper.Set("tfe_api_token", tfeAPIToken)
configPath := ConfigPath()
viper.SetConfigFile(configPath)
err = viper.WriteConfig()
if err != nil {
log.Fatal("Failed to write to: ", configPath, " Error was: ", err)
}
fmt.Println("Saved to", configPath)
}
So what can I pass to this method to test that the output is as expected?
package cmd
import (
"strings"
"testing"
)
func TestCreateConfigFileFromPrompts(t *testing.T) {
// How do I pass the stdin and out to the method?
// Then how do I test their contents?
// CreateConfigFileFromPrompts()
}
func TestCreateConfigFileFromPrompts(t *testing.T) {
var in bytes.Buffer
var gotOut, wantOut bytes.Buffer
// The reader should read to the \n each of two times.
in.Write([]byte("example-url.com\nexampletoken\n"))
// wantOut could just be []byte, but for symmetry's sake I've used another buffer
wantOut.Write([]byte("TFE URL:TFE API Token (Create one at example-url.com/app/settings/tokens)"))
// I don't know enough about Viper to manage ConfigPath()
// but it seems youll have to do it here somehow.
configFilePath := "test/file/location"
CreateConfigFileFromPrompts(&in, &gotOut)
// verify that correct prompts were sent to the writer
if !bytes.Equal(gotOut.Bytes(), wantOut.Bytes()) {
t.Errorf("Prompts = %s, want %s", gotOut.Bytes(), wantOut.Bytes())
}
// May not need/want to test viper's writing of the config file here, or at all, but if so:
var fileGot, fileWant []byte
fileWant = []byte("Correct Config file contents:\n URL:example-url.com\nTOKEN:exampletoken")
fileGot, err := ioutil.ReadFile(configFilePath)
if err != nil {
t.Errorf("Error reading config file %s", configFilePath)
}
if !bytes.Equal(fileGot, fileWant) {
t.Errorf("ConfigFile: %s not created correctly got = %s, want %s", configFilePath, fileGot, fileWant)
}
}
As highlighted by #zdebra in comments to his answer, the go-input package is panicing and giving you the error: Reader must be a file. If you are married to using that package, you can avoid the problem by disabling the masking option on the ui.Ask for your second input:
tfeAPIToken, err := ui.Ask(fmt.Sprintf("TFE API Token (Create one at %s/app/settings/tokens)", tfeURL), &input.Options{
Default: "",
Required: true,
Loop: true,
//Mask: true, // if this is set to True, the input must be a file for some reason
//MaskDefault: true,
})
The reader and the writer need to be set up before the tested function is called. After is called, the result is written into the writer where it should be verified.
package cmd
import (
"strings"
"testing"
)
func TestCreateConfigFileFromPrompts(t *testing.T) {
in := strings.NewReader("<your input>") // you can use anything that satisfies io.Reader interface here
out := new(strings.Builder) // you could use anything that satisfies io.Writer interface here like bytes.Buffer
CreateConfigFileFromPrompts(in, out)
// here you verify the output written into the out
expectedOutput := "<your expected output>"
if out.String() != expectedOutput {
t.Errorf("expected %s to be equal to %s", out.String(), expectedOutput)
}
}

String and integer concatenation issues golang

I am trying to write a port scanner in Go, i am facing few problems since i am new to this. Below is the code i have written till now.
package main
import (
"fmt"
"log"
"net"
"os"
)
func main() {
callme()
}
func callme() {
var status string
getip := os.Args[1]
getport := 0
for i := 0; i < 10; i++ {
getport += i
data := getip + ":" + getport
conn, err := net.Dial("tcp", data)
if err != nil {
log.Println("Connection error:", err)
status = "Unreachable"
} else {
status = getport + " - " + "Open"
defer conn.Close()
}
fmt.Println(status)
}
}
I take ip from user as a command line arg, and then want to scan all ports on this ip. Since the net.Dial function needs data in a format like "ip:port" i am kinda confused how to concat string and int each time. Can any1 help me achieve this ?
One possibility is using strconv.Itoa(getport) to convert the int into a string. Another possibility is formatting the string, as in fmt.Sprintf("%s:%d", getip, getport) or fmt.Sprintf("%d - Open", getport).

Resources