Tasks inside chromedp.ActionFunc not working as expected - go

This is my code:
package main
import (
"context"
"log"
"fmt"
"github.com/chromedp/chromedp"
)
func main() {
queries := [3]string{"Object.keys(window);", "window.CSS", "window.Array"}
// create context
ctx, cancel := chromedp.NewContext(context.Background())
defer cancel()
// run task list
var res []byte
err := chromedp.Run(ctx,
chromedp.Navigate(`https://www.google.com/`),
chromedp.ActionFunc(func(ctx context.Context) error {
for _, query := range queries {
err2 := chromedp.Evaluate(query, &res)
if err2 != nil {
fmt.Printf("error in ActionFunc: %s\n", err2)
}
fmt.Printf("Query %s outputs: %v\n", query, res)
}
return nil
}),
)
if err != nil {
log.Fatal(err)
}
}
What i am trying to do is to navigate to url to Evaluate and get values for a big list of queries (I reduced the array to 3 queries for the example).
and then it should just outputs the values from those queries.
But what i get is these errors for every iteration:
error in ActionFunc: %!s(chromedp.ActionFunc=0x7f25a0)
Query Object.keys(window); outputs: []
error in ActionFunc: %!s(chromedp.ActionFunc=0x7f25a0)
Query window.CSS outputs: []
error in ActionFunc: %!s(chromedp.ActionFunc=0x7f25a0)
Query window.Array outputs: []

chromedp.Evaluate does not return error. It returns EvaluateAction. It has Do func which accepts context. So you can try this;
queries := [3]string{"Object.keys(window);", "window.CSS", "window.Array"}
// create context
ctx, cancel := chromedp.NewContext(context.Background())
defer cancel()
// run task list
var res []byte
err := chromedp.Run(ctx,
chromedp.Navigate(`https://www.google.com/`),
chromedp.WaitReady("body"),
//chromedp.Evaluate("Object.keys(window)", &res),
chromedp.ActionFunc(func(ctx context.Context) error {
for _, query := range queries {
chromedp.Evaluate(query, &res).Do(ctx)
fmt.Printf("Query %s outputs: %+v\n", query, string(res))
}
return nil
}),
)
if err != nil {
log.Fatal(err)
}

Related

RethinkDB r.DBList() gives blank object in return

I'm using this golang code to check list of databases in RethinkDB, and getting no lists in return.
package main
import (
"encoding/json"
"fmt"
"log"
r "gopkg.in/rethinkdb/rethinkdb-go.v6"
)
func main() {
log.SetFlags(0)
rdbOpts := r.ConnectOpts{
Address: "localhost:28015",
}
rconn, err := r.Connect(rdbOpts)
checkError(err)
res, err := r.DBList().Run(rconn)
checkError(err)
printObj(res)
}
func checkError(err error) {
if err != nil {
log.Println(err)
return
}
}
func printObj(v interface{}) {
vBytes, _ := json.Marshal(v)
fmt.Println(string(vBytes))
}
Result:
$ go run main.go
{}
This is just a fresh started Rethinkdb instance on local machine, which if queried through Data Explorer from web ui indeed returns the following answer for query r.dbList()
[
"rethinkdb" ,
"test"
]
What am I doing wrong in my query? I know it must be something small, as it's just basic query.
Appreciate any pointers or help. Thanks
I see what I was doing wrong. 🤦‍♂️
I didn't process the response to show rows data as per RethinkDb docs.
Here's the working main() func code:
func main() {
rdbOpts := r.ConnectOpts{
Address: "localhost:28015",
}
rconn, err := r.Connect(rdbOpts)
checkError(err)
res, err := r.DBList().Run(rconn)
checkError(err)
printObj(res)
var row []interface{}
err2 := res.All(&row)
if err2 == r.ErrEmptyResult {
// row not found
}
if err2 != nil {
// error
}
if row != nil {
jsonData, _ := json.Marshal(row)
fmt.Println("total number of rows:", len(row))
fmt.Println("row map obj:", row)
fmt.Println("row JSON output:", string(jsonData))
} else {
fmt.Println("No rows returned")
}
}
Output:
$ go run main.go
{}
total number of rows: 2
row map obj: [rethinkdb test]
row JSON output: ["rethinkdb","test"]

Best approach to getting results out of goroutines

I have two functions that I cannot change (see first() and second() below). They are returning some data and errors (the output data is different, but in the examples below I use (string, error) for simplicity)
I would like to run them in separate goroutines - my approach:
package main
import (
"fmt"
"os"
)
func first(name string) (string, error) {
if name == "" {
return "", fmt.Errorf("empty name is not allowed")
}
fmt.Println("processing first")
return fmt.Sprintf("First hello %s", name), nil
}
func second(name string) (string, error) {
if name == "" {
return "", fmt.Errorf("empty name is not allowed")
}
fmt.Println("processing second")
return fmt.Sprintf("Second hello %s", name), nil
}
func main() {
firstCh := make(chan string)
secondCh := make(chan string)
go func() {
defer close(firstCh)
res, err := first("one")
if err != nil {
fmt.Printf("Failed to run first: %v\n", err)
}
firstCh <- res
}()
go func() {
defer close(secondCh)
res, err := second("two")
if err != nil {
fmt.Printf("Failed to run second: %v\n", err)
}
secondCh <- res
}()
resultsOne := <-firstCh
resultsTwo := <-secondCh
// It's important for my app to do error checking and stop if errors exist.
if resultsOne == "" || resultsTwo == "" {
fmt.Println("There was an ERROR")
os.Exit(1)
}
fmt.Println("ONE:", resultsOne)
fmt.Println("TWO:", resultsTwo)
}
I believe one caveat is that resultsOne := <- firstCh blocks until first goroutine finishes, but I don't care too much about this.
Can you please confirm that my approach is good? What other approaches would be better in my situation?
The example looks mostly good. A couple improvements are:
declaring your channels as buffered
firstCh := make(chan string, 1)
secondCh := make(chan string, 1)
With unbuffered channels, send operations block (until someone receives). If your goroutine #2 is much faster than the first, it will have to wait until the first finishes as well, since you receive in sequence:
resultsOne := <-firstCh // waiting on this one first
resultsTwo := <-secondCh // sender blocked because the main thread hasn't reached this point
use "golang.org/x/sync/errgroup".Group. The program will feel "less native" but it dispenses you from managing channels by hand — which trades, in a non-contrived setting, for sync'ing writes on the results:
func main() {
var (
resultsOne string
resultsTwo string
)
g := errgroup.Group{}
g.Go(func() error {
res, err := first("one")
if err != nil {
return err
}
resultsOne = res
return nil
})
g.Go(func() error {
res, err := second("two")
if err != nil {
return err
}
resultsTwo = res
return nil
})
err := g.Wait()
// ... handle err

How to pass variadic functions into another function

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)

Simulate an HTTP request with success or failure using retry logic

I want to simulate a re-try option with http like:
first two http attempts with error (using some faulty urls)
the third with success (with valid url)
This is a bit tricky any idea how to do it? I try with loop on the doSomething method with different url but it doesn't make the point,
which is for example, retry at least 3 times until you get http 200, (success) any idea how could I simulate it?
maybe run in loop on following...
www.stackoverflow.com2
www.stackoverflow.com1
www.stackoverflow.com
https://play.golang.org/p/dblPh1T0XBu
package main
import (
`fmt`
`log`
"net/http"
`time`
`github.com/cenkalti/backoff/v4`
)
func main() {
b := backoff.NewExponentialBackOff()
b.MaxElapsedTime = 3 * time.Second
retryable := func() error {
val, err := doSomething("https://www.google.com1")
if err != nil {
return err
}
fmt.Println(val)
return nil
}
notify := func(err error, t time.Duration) {
log.Printf("error: %v happened at time: %v", err, t)
}
err := backoff.RetryNotify(retryable, b, notify)
if err != nil {
fmt.Errorf("error after retrying: %v", err)
}
}
func doSomething(url string) (int, error) {
res, e := http.Get(url)
if e != nil {
fmt.Println("error occurred: ", e)
return 500, e
}
return res.StatusCode, nil
}
The idea on the comment below is part of the problem, I need to use the http calls
https://play.golang.org/p/FTR7J2r-QB7
package main
import (
`fmt`
`log`
`time`
`github.com/cenkalti/backoff/v4`
)
func main() {
b := backoff.NewExponentialBackOff()
b.MaxElapsedTime = 3 * time.Second
retrybuilder := func (count int) func() error {
return func() error {
var succeed bool
count -= 1
if count == 0 {
succeed = true
}
val, err := doSomething(succeed)
if err != nil {
fmt.Println("response: ", val)
}
return err
}
}
notify := func(err error, t time.Duration) {
log.Printf("error: %v happened at time: %v", err, t)
}
err := backoff.RetryNotify(retrybuilder(3), b, notify)
if err != nil {
fmt.Printf("error after retrying: %v", err)
}
}
func doSomething(succeed bool) (int, error) {
if !succeed {
return 500, fmt.Errorf("E_SIMULATED: sim error")
}
return 200, nil
}

golang http handler context

I'm trying to understand variable scopes in golang with the following code.
In this example, calling in http a page will echo the uri query combined with a stored value in Boltdb.
The problem is that the database driver doesn't seem to run correctly in the http handler context: it doesn't print anything to stdout nor to the http request.
I was expecting it to print :
He's loving <'uri query content'> but prefers pizza (data from bolt.db driver)
How to fix this code?
package main
import (
"fmt"
"net/http"
"log"
"github.com/boltdb/bolt"
)
var db bolt.DB
func handler(w http.ResponseWriter, r *http.Request) {
dberr := db.Update(func(tx *bolt.Tx) error {
log.Println("here")
b := tx.Bucket([]byte("MyBucket"))
loving := b.Get([]byte("loving"))
log.Printf("He's loving %s but prefers %s",r.URL.Path[1:], string(loving))
fmt.Fprintf(w,"He's loving %s but prefers %s",r.URL.Path[1:], string(loving) )
return nil
})
if dberr != nil {
fmt.Errorf("db update: %s", dberr)
}
log.Printf("Finished handling")
}
func main() {
db, err := bolt.Open("my.db", 0600, nil)
if err != nil {
log.Fatal(err)
}else{
log.Println("database opened")
}
dberr := db.Update(func(tx *bolt.Tx) error {
b, err := tx.CreateBucketIfNotExists([]byte("MyBucket"))
if err != nil {
return fmt.Errorf("create bucket: %s", err)
}
err2 := b.Put([]byte("loving"), []byte("pizza"))
if err2 != nil {
return fmt.Errorf("put loving: %s", err2)
}
loving := b.Get([]byte("loving"))
log.Printf("He's loving %s", string(loving))
return nil
})
if dberr != nil {
fmt.Errorf("db update: %s", err)
}
defer db.Close()
http.HandleFunc("/", handler)
http.ListenAndServe(":8080", nil)
}
I think I see your bug. This one is usually a little difficult to track because its just the : in front of the equals. It was basically a scoping issue because you declared db as a global while at the same time creating a db variable that was scoped to your main function.
You used db, err := ... to assign the values instead of just =. := will both declare and infer the type. Since its also doing declaration, the db you're using in the main function is not the db you have declared in the global scope. Meanwhile the handler is still attempting to use the db that was declared in the global scope. The below code is the same code as you initially had with a few comments in the code to outline what the working changes are. Hope this helps!
package main
import (
"fmt"
"log"
"net/http"
"github.com/boltdb/bolt"
)
var db *bolt.DB // this is going to be a pointer and is going to be nil until its set by the main function
func handler(w http.ResponseWriter, r *http.Request) {
dberr := db.Update(func(tx *bolt.Tx) error {
log.Println("here")
b := tx.Bucket([]byte("MyBucket"))
loving := b.Get([]byte("loving"))
log.Printf("He's loving %s but prefers %s", r.URL.Path[1:], string(loving))
fmt.Fprintf(w, "He's loving %s but prefers %s", r.URL.Path[1:], string(loving))
return nil
})
if dberr != nil {
fmt.Errorf("db update: %s", dberr)
}
log.Printf("Finished handling")
}
func main() {
var err error // this will have to be declared because of the next line to assign db the first value returned from `bolt.Open`
db, err = bolt.Open("my.db", 0600, nil) // notice that this has changed and is no longer `db, err := ...` rather its `db, err = ...`
if err != nil {
log.Fatal(err)
} else {
log.Println("database opened")
}
dberr := db.Update(func(tx *bolt.Tx) error {
b, err := tx.CreateBucketIfNotExists([]byte("MyBucket"))
if err != nil {
return fmt.Errorf("create bucket: %s", err)
}
err2 := b.Put([]byte("loving"), []byte("pizza"))
if err2 != nil {
return fmt.Errorf("put loving: %s", err2)
}
loving := b.Get([]byte("loving"))
log.Printf("He's loving %s", string(loving))
return nil
})
if dberr != nil {
fmt.Errorf("db update: %s", err)
}
defer db.Close()
http.HandleFunc("/", handler)
http.ListenAndServe(":3000", nil)
}

Resources