How does one programmatically subscribe an SQS queue to an SNS topic in Go? - aws-sdk-go

Here is what I tried, using version 53eb8b070e9a5067829fd029539966181632032a of aws-sdk-go.
// main.go
package main
import (
"errors"
"fmt"
"log"
"net/http"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/sns"
"github.com/aws/aws-sdk-go/service/sqs"
)
func main() {
if err := makeTopicAndQueue(); err != nil {
log.Fatalf("aws-sns-sqs: %v", err)
}
}
func makeTopicAndQueue() error {
sess, err := session.NewSession(&aws.Config{
HTTPClient: &http.Client{},
Region: aws.String("us-east-2"),
Credentials: nil,
MaxRetries: aws.Int(0),
})
log.Printf("Creating an SNS topic.")
snsClient := sns.New(sess, &aws.Config{})
topicName := "test-topic"
out, err := snsClient.CreateTopic(&sns.CreateTopicInput{Name: aws.String(topicName)})
if err != nil {
return fmt.Errorf(`creating topic "%s": %v`, topicName, err)
}
defer snsClient.DeleteTopic(&sns.DeleteTopicInput{TopicArn: out.TopicArn})
log.Printf("Creating an SQS queue.")
sqsClient := sqs.New(sess, &aws.Config{})
subName := "test-subscription"
out2, err := sqsClient.CreateQueue(&sqs.CreateQueueInput{QueueName: aws.String(subName)})
if err != nil {
return fmt.Errorf(`creating subscription queue "%s": %v`, subName, err)
}
log.Printf("Getting queue ARN.")
out3, err := sqsClient.GetQueueAttributes(&sqs.GetQueueAttributesInput{
QueueUrl: out2.QueueUrl,
AttributeNames: []*string{aws.String("QueueArn")},
})
if err != nil {
return fmt.Errorf("getting queue ARN for %s: %v", *out2.QueueUrl, err)
}
qARN := out3.Attributes["QueueArn"]
log.Printf("Subscribing the queue to the topic.")
_, err = snsClient.Subscribe(&sns.SubscribeInput{
TopicArn: out.TopicArn,
Endpoint: qARN,
Protocol: aws.String("sqs"),
})
if err != nil {
return fmt.Errorf("subscribing: %v", err)
}
log.Printf("Getting the confirmation token from the queue.")
out4, err := sqsClient.ReceiveMessage(&sqs.ReceiveMessageInput{
QueueUrl: out2.QueueUrl,
})
if err != nil {
return fmt.Errorf("receiving subscription confirmation message from queue: %v", err)
}
ms := out4.Messages
var token *string
switch len(ms) {
case 0:
return errors.New("no subscription confirmation message found in queue")
case 1:
m := ms[0]
token = m.Body
default:
return fmt.Errorf("%d messages found in queue, want exactly 1", len(ms))
}
log.Printf("Using the token to finish subscribing.")
_, err = snsClient.ConfirmSubscription(&sns.ConfirmSubscriptionInput{
TopicArn: out.TopicArn,
Token: token,
})
if err != nil {
return fmt.Errorf("confirming subscription: %v", err)
}
sqsClient.DeleteQueue(&sqs.DeleteQueueInput{QueueUrl: out2.QueueUrl})
return nil
}
I expected it to get to the end but it failed with this output:
[ ~/src/aws-sqs-issue ] go run main.go
2019/01/15 09:31:19 Creating an SNS topic.
2019/01/15 09:31:19 Creating an SQS queue.
2019/01/15 09:31:20 Getting queue ARN.
2019/01/15 09:31:20 Subscribing the queue to the topic.
2019/01/15 09:31:21 Getting the confirmation token from the queue.
2019/01/15 09:31:21 aws-sns-sqs: no subscription confirmation message found in queue
Am I doing something wrong or is this a bug in the SDK?
I'm not sure what else to say about this. Here is some additional verbiage to somehow get the warning about the post being mostly code to go away. It's best to stop reading at this point because all the rest of this is bound to make for boring reading. I don't know much much longer I can keep on making up nonsense to satisfy this silly algorithm. Why don't they allow a simple post that contains a lot of code? I have no idea. Ah well. There is an upside down gopher on my desk. I think it was intentional. Due to the poor critter's anatomy, he's doing more of an eyeball-stand than a head-stand. My desk plant didn't hold up too well over the holidays. Better give it some water. Wowee, this thing really wants an awful lot of words. Well, I aim to please. If I keep going, will I accidentally output some Shakespeare? Well, it's over, thank the Bard.

This one is quite old, but here is the trick to solve it:
For an Amazon SNS topic to be able to send messages to a queue, you must set a policy on the queue that allows the Amazon SNS topic to perform the sqs:SendMessage action. See more
To do so using the current version of the V1 SDK, when creating the Queue you must define the Policy manually as a Queue attribute, allowing the SNS Topic to send messages to your SQS Queue.
Here is an example as code:
queueARN := fmt.Sprintf("arn:aws:sqs:%s:%s:%s", "us-east-1", "xxx", "my-queue")
topicARN := fmt.Sprintf("arn:aws:sns:%s:%s:%s", "us-east-1", "xxx", "my-topic")
_, err := b.sqs.CreateQueue(&sqs.CreateQueueInput{
QueueName: aws.String("my-queue"),
Attributes: map[string]*string{
"Policy": aws.String(fmt.Sprintf(`{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "SNSTopicSendMessage",
"Effect": "Allow",
"Principal": "*",
"Action": "SQS:SendMessage",
"Resource": "%s",
"Condition": {
"ArnEquals": {
"aws:SourceArn": "%s"
}
}
}
]
}`, queueARN, b.eventsTopicARN)),
},
})
if err != nil {
return err
}

Related

Cadence workflow not executing activities after introducing versioning

I introduced cadence versioning in to cadence workflow and after that the workflow has stopped exeucting at the point of versioning introduction.
I am receiving the following error :
2020-10-29T07:23:49.587Z DEBUG internal/internal_event_handlers.go:465 ExecuteActivity {"Domain":
"domain_1", "TaskList": "tasklist_1", "WorkerID":
"6#cdnc-5ddb9ccbb5-5dt5j#tasklist", "WorkflowType":
"do_work_workflow", "WorkflowID": "CREATE", "RunID":
"cab97b65-9892-48c5-b842-3f8b462d8602", "ActivityID": "4",
"ActivityType": "do_Task_D"}
2020-10-29T07:23:49.620Z DEBUG internal/internal_task_handlers.go:1077 Cached state staled, new task has unexpected events {"Domain": "domain_1",
"TaskList": "tasklist_1", "WorkerID":
"6#cdnc-5ddb9ccbb5-5dt5j#tasklist1", "WorkflowID": "CREATE", "RunID":
"cab97b65-9892-48c5-b842-3f8b462d8602",
"CachedPreviousStartedEventID": 30, "TaskFirstEventID": 22,
"TaskStartedEventID": 30, "TaskPreviousStartedEventID": 21}
My workflow code will look like this:
func doWorkflow(ctx workflow.Context, input string) error {
err := doTaskA(input)
if err != nil {
return err
}
err = doTaskB(input)
if err != nil {
return err
}
versionTaskC := workflow.GetVersion(ctx, "ChangeID", workflow.DefaultVersion, 1)
if versionTaskC == workflow.DefaultVersion {
err = doTaskC(input)
if err != nil {
return err
}
} else {
err = doTaskD(input)
if err != nil {
return err
}
}
err = doTaskD2(input)
if err != nil {
return err
}
err = doTaskD3(input)
if err != nil {
return err
}
return nil
}
At the ChangeID , version returned is 1, and the workflow tries to execute TaskD but, it is not executing it, It is stuck in an infinite loop, trying to execute the TaskD.
The error message I get is
Cached state staled, new task has unexpected events
and
BadRequestError{Message: CadenceChangeVersion is not valid search
attribute}
Can you please help me with this issue ?
there are two possibles:
You are running a Cadence server with version lower than 0.11.
[very likely]Your Cadence server upgraded from a lower version but didn't make the elasticSearch schema change:
cadence admin cluster add-search-attr --search_attr_key CadenceChangeVersion --search_attr_type 1
And you probably also need to add this:
cadence admin cluster add-search-attr --search_attr_key BinaryChecksums --search_attr_type 1
Background:
CadenceChangeVersion is introduced in https://github.com/uber/cadence/releases/tag/v0.11.0
It's to help searching for workflow change versions.
Let me know if this is not correct.

Write on a closed net.Conn but returned nil error

Talk is cheap, so here we go the simple code:
package main
import (
"fmt"
"time"
"net"
)
func main() {
addr := "127.0.0.1:8999"
// Server
go func() {
tcpaddr, err := net.ResolveTCPAddr("tcp4", addr)
if err != nil {
panic(err)
}
listen, err := net.ListenTCP("tcp", tcpaddr)
if err != nil {
panic(err)
}
for {
if conn, err := listen.Accept(); err != nil {
panic(err)
} else if conn != nil {
go func(conn net.Conn) {
buffer := make([]byte, 1024)
n, err := conn.Read(buffer)
if err != nil {
fmt.Println(err)
} else {
fmt.Println(">", string(buffer[0 : n]))
}
conn.Close()
}(conn)
}
}
}()
time.Sleep(time.Second)
// Client
if conn, err := net.Dial("tcp", addr); err == nil {
for i := 0; i < 2; i++ {
_, err := conn.Write([]byte("hello"))
if err != nil {
fmt.Println(err)
conn.Close()
break
} else {
fmt.Println("ok")
}
// sleep 10 seconds and re-send
time.Sleep(10*time.Second)
}
} else {
panic(err)
}
}
Ouput:
> hello
ok
ok
The Client writes to the Server twice. After the first read, the Server closes the connection immediately, but the Client sleeps 10 seconds and then re-writes to the Server with the same already closed connection object(conn).
Why can the second write succeed (returned error is nil)?
Can anyone help?
PS:
In order to check if the buffering feature of the system affects the result of the second write, I edited the Client like this, but it still succeeds:
// Client
if conn, err := net.Dial("tcp", addr); err == nil {
_, err := conn.Write([]byte("hello"))
if err != nil {
fmt.Println(err)
conn.Close()
return
} else {
fmt.Println("ok")
}
// sleep 10 seconds and re-send
time.Sleep(10*time.Second)
b := make([]byte, 400000)
for i := range b {
b[i] = 'x'
}
n, err := conn.Write(b)
if err != nil {
fmt.Println(err)
conn.Close()
return
} else {
fmt.Println("ok", n)
}
// sleep 10 seconds and re-send
time.Sleep(10*time.Second)
} else {
panic(err)
}
And here is the screenshot:
attachment
There are several problems with your approach.
Sort-of a preface
The first one is that you do not wait for the server goroutine
to complete.
In Go, once main() exits for whatever reason,
all the other goroutines still running, if any, are simply
teared down forcibly.
You're trying to "synchronize" things using timers,
but this only works in toy situations, and even then it
does so only from time to time.
Hence let's fix your code first:
package main
import (
"fmt"
"log"
"net"
"time"
)
func main() {
addr := "127.0.0.1:8999"
tcpaddr, err := net.ResolveTCPAddr("tcp4", addr)
if err != nil {
log.Fatal(err)
}
listener, err := net.ListenTCP("tcp", tcpaddr)
if err != nil {
log.Fatal(err)
}
// Server
done := make(chan error)
go func(listener net.Listener, done chan<- error) {
for {
conn, err := listener.Accept()
if err != nil {
done <- err
return
}
go func(conn net.Conn) {
var buffer [1024]byte
n, err := conn.Read(buffer[:])
if err != nil {
log.Println(err)
} else {
log.Println(">", string(buffer[0:n]))
}
if err := conn.Close(); err != nil {
log.Println("error closing server conn:", err)
}
}(conn)
}
}(listener, done)
// Client
conn, err := net.Dial("tcp", addr)
if err != nil {
log.Fatal(err)
}
for i := 0; i < 2; i++ {
_, err := conn.Write([]byte("hello"))
if err != nil {
log.Println(err)
err = conn.Close()
if err != nil {
log.Println("error closing client conn:", err)
}
break
}
fmt.Println("ok")
time.Sleep(2 * time.Second)
}
// Shut the server down and wait for it to report back
err = listener.Close()
if err != nil {
log.Fatal("error closing listener:", err)
}
err = <-done
if err != nil {
log.Println("server returned:", err)
}
}
I've spilled a couple of minor fixes
like using log.Fatal (which is
log.Print + os.Exit(1)) instead of panicking,
removed useless else clauses to adhere to the coding standard of keeping the main
flow where it belongs, and lowered the client's timeout.
I have also added checking for possible errors Close on sockets may return.
The interesting part is that we now properly shut the server down by closing the listener and then waiting for the server goroutine to report back (unfortunately Go does not return an error of a custom type from net.Listener.Accept in this case so we can't really check that Accept exited because we've closed the listener).
Anyway, our goroutines are now properly synchronized, and there is
no undefined behaviour, so we can reason about how the code works.
Remaining problems
Some problems still remain.
The more glaring is you making wrong assumption that TCP preserves
message boundaries—that is, if you write "hello" to the client
end of the socket, the server reads back "hello".
This is not true: TCP considers both ends of the connection
as producing and consuming opaque streams of bytes.
This means, when the client writes "hello", the client's
TCP stack is free to deliver "he" and postpone sending "llo",
and the server's stack is free to yield "hell" to the read
call on the socket and only return "o" (and possibly some other
data) in a later read.
So, to make the code "real" you'd need to somehow introduce these
message boundaries into the protocol above TCP.
In this particular case the simplest approach would be either
using "messages" consisting of a fixed-length and agreed-upon
endianness prefix indicating the length of the following
data and then the string data itself.
The server would then use a sequence like
var msg [4100]byte
_, err := io.ReadFull(sock, msg[:4])
if err != nil { ... }
mlen := int(binary.BigEndian.Uint32(msg[:4]))
if mlen < 0 {
// handle error
}
if mlen == 0 {
// empty message; goto 1
}
_, err = io.ReadFull(sock, msg[5:5+mlen])
if err != nil { ... }
s := string(msg[5:5+mlen])
Another approach is to agree on that the messages do not contain
newlines and terminate each message with a newline
(ASCII LF, \n, 0x0a).
The server side would then use something like
a usual bufio.Scanner loop to get
full lines from the socket.
The remaining problem with your approach is to not dealing with
what Read on a socket returns: note that io.Reader.Read
(that's what sockets implement, among other things) is allowed
to return an error while having had read some data from the
underlying stream. In your toy example this might rightfully
be unimportant, but suppose that you're writing a wget-like
tool which is able to resume downloading of a file: even if
reading from the server returned some data and an error, you
have to deal with that returned chunk first and only then
handle the error.
Back to the problem at hand
The problem presented in the question, I beleive, happens simply because in your setup you hit some TCP buffering problem due to the tiny length of your messages.
On my box which runs Linux 4.9/amd64 two things reliably "fix"
the problem:
Sending messages of 4000 bytes in length: the second call
to Write "sees" the problem immediately.
Doing more Write calls.
For the former, try something like
msg := make([]byte, 4000)
for i := range msg {
msg[i] = 'x'
}
for {
_, err := conn.Write(msg)
...
and for the latter—something like
for {
_, err := conn.Write([]byte("hello"))
...
fmt.Println("ok")
time.Sleep(time.Second / 2)
}
(it's sensible to lower the pause between sending stuff in
both cases).
It's interesting to note that the former example hits the
write: connection reset by peer (ECONNRESET in POSIX)
error while the second one hits write: broken pipe
(EPIPE in POSIX).
This is because when we're sending in chunks worth 4k bytes,
some of the packets generated for the stream manage to become
"in flight" before the server's side of the connection manages
to propagate the information on its closure to the client,
and those packets hit an already closed socket and get rejected
with the RST TCP flag set.
In the second example an attempt to send another chunk of data
sees that the client side already knows that the connection
has been teared down and fails the sending without "touching
the wire".
TL;DR, the bottom line
Welcome to the wonderful world of networking. ;-)
I'd recommend buying a copy of "TCP/IP Illustrated",
read it and experiment.
TCP (and IP and other protocols above IP)
sometimes works not like people expect them to by applying
their "common sense".

Very Sporadic Go HTTP Error: multiple response.WriteHeader calls

I wrote Kanali which is an open source Kubernetes Ingress/API management tool and for about 1/200k requests I receive the following fatal error:
2017/08/16 12:40:57 http: multiple response.WriteHeader calls
{"level":"error","method":"GET","msg":"unknown error","time":"2017-08-16T12:40:57Z","uri":"/ommitted/path"}
{"level":"fatal","msg":"write tcp4 192.168.2.160:8443-\u003e192.168.4.0:54554: write: broken pipe","time":"2017-08-16T12:40:57Z"}
I'm having a really hard time reproducing it but here is the relevant code. Kanali is a large project but the td;lr is that after this first code snippet is executed, the second code snipped is executed which handles errors.
func (step WriteResponseStep) Do(ctx context.Context, m *metrics.Metrics, c *controller.Controller, w http.ResponseWriter, r *http.Request, resp *http.Response, trace opentracing.Span) error {
for k, v := range resp.Header {
for _, value := range v {
w.Header().Set(k, value)
}
}
closer, str, err := utils.DupReaderAndString(resp.Body)
if err != nil {
logrus.Errorf("error copying response body, response may not be as expected: %s", err.Error())
}
trace.SetTag("http.status_code", resp.StatusCode)
trace.SetTag("http.response_body", str)
w.WriteHeader(resp.StatusCode)
if _, err := io.Copy(w, closer); err != nil {
return err
}
return nil
}
later in the code...
if err != nil {
w.Header().Set("Content-Type", "application/json")
switch e := err.(type) {
case utils.Error:
logrus.WithFields(logrus.Fields{
"method": r.Method,
"uri": r.RequestURI,
}).Error(e.Error())
errStatus, err := json.Marshal(utils.JSONErr{Code: e.Status(), Msg: e.Error()})
if err != nil {
logrus.Warnf("could not marsah request headers into JSON - tracing data maybe not be as expected")
}
w.WriteHeader(e.Status())
if err := json.NewEncoder(w).Encode(utils.JSONErr{Code: e.Status(), Msg: e.Error()}); err != nil {
logrus.Fatal(err.Error())
}
default:
logrus.WithFields(logrus.Fields{
"method": r.Method,
"uri": r.RequestURI,
}).Error("unknown error")
errStatus, err := json.Marshal(utils.JSONErr{Code: http.StatusInternalServerError, Msg: "unknown error"})
if err != nil {
logrus.Warnf("could not marsah request headers into JSON - tracing data maybe not be as expected")
}
w.WriteHeader(http.StatusInternalServerError)
if err := json.NewEncoder(w).Encode(utils.JSONErr{Code: http.StatusInternalServerError, Msg: "unknown error"}); err != nil {
logrus.Fatal(err.Error())
}
}
}
The error message is telling you that WriteHeader is called more than once (either directly or indirectly by a call to Write). The header can only be written to the network once. Both snippets have a call to WriteHeader.

Periodically polling a REST endpoint in Go

I am trying to write a Go application that periodically polls a REST endpoint exposed by a PHP application. The Go polling application reads the payload into a struct and does further processing. I am looking for some recommendations for starting the implementation.
Simplest way would be to use a Ticker:
ticker := time.NewTicker(time.Second * 1).C
go func() {
for {
select {
case <- ticker:
response,_ := http.Get("http://...")
_, err := io.Copy(os.Stdout, response.Body)
if err != nil {
log.Fatal(err)
}
response.Body.Close()
}
}
}()
time.Sleep(time.Second * 10)

Go paypal REST API request

So Im currently using Go and Im trying to create a payment for Paypal I been trying this code
payer := &Payer{"paypal"}
amount := &Amount{"EUR", "12"}
trans := &Transactions{amount, "A super test"}
uris := &Redirect_urls{"http://localhost", "http://localhost"}
p := &Payment{"sale", payer, trans, uris}
response, err := json.Marshal(p)
if err != nil {
log.Println("Error at PaypalPayment - buy controller")
log.Fatal(err)
}
log.Println(string(response))
client := &http.Client{}
buf := bytes.NewBuffer(response)
req, err := http.NewRequest("POST", "https://api.sandbox.paypal.com/v1/payments/payment", buf)
if err != nil {
log.Println("Error at PaypalPayment - buy controller - 2")
log.Fatal(err)
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", "Bearer " + token.Access_token)
resp, err := client.Do(req)
if err != nil {
log.Println("Error at PaypalPayment - buy controller - 3")
log.Fatal(err)
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Println("Error at PaypalPayment - buy controller - 4")
log.Fatal(err)
}
log.Println(string(body))
I already got the access token, the problem is Im getting this error on the response body (last line)
MALFORMED_REQUEST
The request Im using is this (as of the println)
{
"Intent":"sale",
"Payer":{
"Payment_method":"paypal"
},
"Transactions":{
"Amount":{
"Currency":"EUR",
"Total":"12"
},
"Description":"Super test"
},
"Redirect_urls":{
"Return_url":"http://localhost",
"Cancel_url":"http://localhost"
}
}
At my eyes seems a good request... no idea what im missing
As pointed out by #jcbwlkr you're casing doesn't match what is in the docs. If you don't have json tags on your types you'll have to add them. You have to keep the property names uppercase in Go because it's what marks the fields as being exported. If you're not familiar with this do a search for 'unexported vs exported fields golang'
For example your Payment structs definition needs to look like this;
type Payment struct {
Amount *Amount `json:"amount"`
Transactions *Transactions `json:"transactions"`
RdUrls *Redirect_urls `json:"redirect_urls"`
}
Also, just fyi you can use nest those declarations where you declare the payment so you don't have to assign to local instances of Amount, Transactions and Redirect_urls in order to do the declaration.
It's just like;
p := &Payment{"sale", payer, &Transactions{amount, "A super test"}, uris}
The problem was transactions needed to be an array. how blind I am
Transactions []*Transactions `json:"transactions"`