I have go lang code to read some json file. It's running fine in local but I created Lambda package and uploaded the package in Lambda. It cannot read the file
import (
"context"
"fmt"
"io/ioutil"
"github.com/aws/aws-lambda-go/lambda"
)
type MyEvent struct {
Name string `json:"name"`
}
func HandleRequest(ctx context.Context, name MyEvent) (string, error) {
jsonBytes, err := ioutil.ReadFile("mappings.json")
fmt.Println(string(jsonBytes))
fmt.Println(err)
return fmt.Sprintf("Hello %s!", name.Name), nil
}
func main() {
lambda.Start(HandleRequest)
}
How to read the file from AWS Lambda? Any idea on this?
I have used your sample code and put in the zip file and also the mappings file that I used to test on AWS lambda. Link to code - https://github.com/nihanthd/stackoverflow/tree/master/lambda
Handler name in AWS lambda is trial
Test Data to trigger the function using AWS lambda event
{
"name": "Vignesh"
}
Commands used to build the executable and create the zip file
$ GOARCH=amd64 GOOS=linux go build trial.go
$ zip trial.zip trial mappings.json
Related
The golang operator started writing an error.
failed to list v1.Secret: secrets is forbidden: User "system:serviceaccount:operator-*****" cannot list resource "secrets" in API group "" in the namespace "namespace-name"
The error appeared after we enabled restrictions on list secret (set resource Names).
Without restrictions, everything works fine.
I am not familiar with golang, but after looking at the source code, I came to the conclusion that the error occurs in this place
if err := g.client.Get(ctx, client.ObjectKey{Name: tokens.Name, Namespace: g.namespace}, &tokens); err != nil {
return nil, errors.WithMessage(err, "failed to query tokens")
Here is a more complete part of the code
package initgeneration
import (
"context"
"encoding/json"
dynatracev1beta1 "github.com/Dynatrace/dynatrace-operator/src/api/v1beta1"
"github.com/Dynatrace/dynatrace-operator/src/config"
"github.com/pkg/errors"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"
)
// InitGenerator manages the init secret generation for the user namespaces.
type InitGenerator struct {
client client.Client
apiReader client.Reader
namespace string
canWatchNodes bool
dynakubeQuery kubeobjects.DynakubeQuery
}
...
func (g *InitGenerator) createSecretConfigForDynaKube(ctx context.Context, dynakube *dynatracev1beta1.DynaKube, kubeSystemUID types.UID, hostMonitoringNodes map[string]string) (*standalone.SecretConfig, error) {
var tokens corev1.Secret
if err := g.client.Get(ctx, client.ObjectKey{Name: tokens.Name, Namespace: g.namespace}, &tokens); err != nil {
return nil, errors.WithMessage(err, "failed to query tokens")
The Get method is used in the package "asus.k8s.io/controller-runtime/pkg/client " , when accessing the K8S API, it requests all the secrets and only then imposes a filter.
I need to get around this limitation
How to get secrets, how to access the K8S API through the client package with a restriction on resourceName?
Controller-runtime client ( "sigs.k8s.io/controller-runtime/pkg/client") goes to its cache when receiving a request, and it updates the cache through the watch operation, i.e. it needs access to all secrets
The solution that helped: disable cache for secrets in "Options"
https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/manager#Options
via ClientDisableCacheFor
func (provider operatorManagerProvider) createOptions(namespace string) ctrl.Options {
return ctrl.Options{
Namespace: namespace,
Scheme: scheme.Scheme,
MetricsBindAddress: metricsBindAddress,
Port: operatorManagerPort,
...
ClientDisableCacheFor: []client.Object{
&corev1.Secret{},
},
}
}
Golang vesion: 1.18.5
Configuration lib:
"github.com/spf13/viper"
github repo
I'm writing a AWS Lambda using Go. It sits behind an AWS APIGateway as a REST API. It sends out POST requests to an external API. I want to config that external API's URL and a few other header params in a configuration file. Found that spf13/viper library can be used for that requirement.
Here's my source tree:
api-root
├──config
├ ├── application.yaml
└──lambda
├── main.go
Here's my main.go,
package main
import (
"fmt"
"github.com/aws/aws-lambda-go/events"
"github.com/aws/aws-lambda-go/lambda"
"github.com/spf13/viper"
"my-api/pkg/handlers"
"os"
)
func main() {
lambda.Start(Handler)
}
func Handler(request events.APIGatewayProxyRequest) (*events.APIGatewayProxyResponse, error) {
// Config
v := viper.New()
v.SetConfigName("application") // config file name without extension
v.SetConfigType("yaml")
v.AddConfigPath(".")
v.AddConfigPath("../config/")
v.AutomaticEnv() // read value ENV variable
err := v.ReadInConfig()
if err != nil {
fmt.Println("fatal error config file: default \n", err)
os.Exit(1)
}
env := v.GetString("app.env")
fmt.Println("Environment : ", env)
return handlers.Join(request)
}
Here's my go build command,
GOARCH=amd64 GOOS=linux go build -mod=vendor -o ./bin/<lambda-function-name> -ldflags="-s -w" ./lambda/main.go
And when I run it locally using sam
sam local start-api -t deployment/template.yml
And then hit the local endpoint using postman, it gives me the following error,
fatal error config file: default
Config File "application" Not Found in "[/var/task /var/config]"
However, when I create a normal go app and run it I get the expected params successfully. Here's my normal go app main.go,
package main
import (
"fmt"
"github.com/spf13/viper"
"os"
)
func main() {
v := viper.New()
v.SetConfigName("application") // config file name without extension
v.SetConfigType("yaml")
v.AddConfigPath(".")
v.AddConfigPath("./config/")
v.AutomaticEnv() // read value ENV variable
err := v.ReadInConfig()
if err != nil {
fmt.Println("fatal error config file: default \n", err)
os.Exit(1)
}
en := v.GetString("app.env")
fmt.Println("Environment : ", en)
}
when I run the app I see the expected print in the console,
➜ go run main.go
Environment : dev
What should I do to get viper working in the lambda?
I had the same problem.
I solved it by creating a layer in Lambda (sam)
AWSTemplateFormatVersion: '2010-09-09'
...
Resources:
Function:
Type: AWS::Serverless::Function
Properties:
...
Layers:
- !Ref Files
Files:
Type: AWS::Serverless::LayerVersion
Properties:
LayerName: config-files-layer
ContentUri: <<YAML FILES DIR>>
CompatibleRuntimes:
- ...
And changing
v.AddConfigPath(".")
to
v.AddConfigPath("/opt")
More: Creating and sharing Lambda layers
Is is possible, and if so how, to let a Command initialize a resource and pass it down to its Subcommands. Image an application that takes its arguments like
$ mycmd db --connect <...> create <...>
$ mycmd db --connect <...> update <...>
This may not be a great example but it illustrates the concept. Here db is some resource that all the subcommands depend on. I would like a single function to be responsible for the initialization of the db resource and then pass the initialized resource down to the subcommands. I can't figure out how to do this with urfave/cli/v2 .
You could do it by creating two separate cli.Apps, one that parses the db part of the arguments just to create a context.Context with context.WithValue and then use that context to create the second cli.App which would parse the remainder of the arguments. I'm sure there's a better way to do it.
I'm grateful for any help!
You can achieve this with context values. You set the value in the Before callback of the parent Command. Below code is copied and modified from the subcommands example:
package main
import (
"context"
"fmt"
"log"
"os"
"github.com/urfave/cli/v2"
)
func main() {
app := &cli.App{
Commands: []*cli.Command{
{
Name: "db",
Before: func(c *cli.Context) error {
db := "example"
c.Context = context.WithValue(c.Context, "db", db)
return nil
},
Subcommands: []*cli.Command{
{
Name: "connect",
Action: func(c *cli.Context) error {
db := c.Context.Value("db").(string) // remember to assert to original type
fmt.Println("sub command:", db)
return nil
},
},
},
},
},
}
err := app.Run(os.Args)
if err != nil {
log.Fatal(err)
}
}
This main uses a string so that you can copy paste and run it. You can replace string with your DB object.
How to test:
$ go build -o example
$ ./example db connect
sub command: example
This is newbie question. The dependencies seems to be on github, and it's pretty obvious from the import, so why run doesn't work?
Error is: no required module provides package github.com/hashicorp/go-getter
package main
import (
"context"
"fmt"
"os"
// Problem with line below, getting error: no required module provides package
getter "github.com/hashicorp/go-getter"
)
func main() {
client := &getter.Client{
Ctx: context.Background(),
//define the destination to where the directory will be stored. This will create the directory if it doesnt exist
Dst: "/tmp/gogetter",
Dir: true,
//the repository with a subdirectory I would like to clone only
Src: "github.com/hashicorp/terraform/examples/cross-provider",
Mode: getter.ClientModeDir,
//define the type of detectors go getter should use, in this case only github is needed
Detectors: []getter.Detector{
&getter.GitHubDetector{},
},
//provide the getter needed to download the files
Getters: map[string]getter.Getter{
"git": &getter.GitGetter{},
},
}
//download the files
if err := client.Get(); err != nil {
fmt.Fprintf(os.Stderr, "Error getting path %s: %v", client.Src, err)
os.Exit(1)
}
//now you should check your temp directory for the files to see if they exist
}
Create a folder somewhere called getter, then create a file
getter/getter.go:
package main
import (
"fmt"
"github.com/hashicorp/go-getter/v2"
)
func main() {
fmt.Println(getter.ErrUnauthorized)
}
Notice I didn't use a name like you specified, as it's redundant in this case. The package is already called getter [1], so you don't need to specify the same name. Then, run:
go mod init getter
go mod tidy
go build
https://pkg.go.dev/github.com/hashicorp/go-getter/v2
I am trying to build a web app with two files.
app.go and main.go are both in the same directory.
app.go
package main
import (
"database/sql"
_ "github.com/go-sql-driver/mysql"
"github.com/gorilla/mux"
)
type App struct {
Router *mux.Router
DB *sql.DB
}
func (a *App) Initialize(username, password, server, port, dbName, cacheAddr, cachePass string){
}
func (a *App) Run(addr string) {
}
main.go
package main
func main() {
a := App{}
// more code here
}
I thought my main.go file would recognize App{} but my editor is complaining that App is undeclared name
Both files are in the same main package but I am not sure what went wrong. Could anyone help me about it? Thank you!
From the comments I assume you run the following command: go run main.go. This will ONLY load code in main.go (and files included with import statements). To tell Go to load all .go files in the current directory, run the following instead:
go run .
Similarly, to tell VSCode to load alll files start it like this:
code .