Skip to content
This repository was archived by the owner on Nov 7, 2019. It is now read-only.

Commit 7512f2b

Browse files
Adding single queue multi topic code
Signed-off-by: Christopher Hein <[email protected]>
1 parent 0610ab8 commit 7512f2b

File tree

6 files changed

+237
-157
lines changed

6 files changed

+237
-157
lines changed

pkg/config/types.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ type Config struct {
1717
Region string
1818
Kubeconfig string
1919
MasterURL string
20+
QueueURL string
21+
QueueARN string
2022
AWSSession *session.Session
2123
AWSClientset awsclient.ServiceoperatorV1alpha1Interface
2224
KubeClientset kubernetes.Interface

pkg/queue/queue.go

Lines changed: 85 additions & 131 deletions
Original file line numberDiff line numberDiff line change
@@ -1,74 +1,19 @@
11
package queue
22

33
import (
4+
"context"
45
"encoding/json"
6+
"os"
7+
58
"github.com/aws/aws-sdk-go/aws"
69
"github.com/aws/aws-sdk-go/aws/session"
710
"github.com/aws/aws-sdk-go/service/sns"
811
"github.com/aws/aws-sdk-go/service/sqs"
912
awsclient "github.com/awslabs/aws-service-operator/pkg/client/clientset/versioned/typed/service-operator.aws/v1alpha1"
1013
"github.com/awslabs/aws-service-operator/pkg/config"
11-
"github.com/awslabs/aws-service-operator/pkg/helpers"
12-
"os"
13-
"strings"
14+
"github.com/awslabs/aws-service-operator/pkg/queuemanager"
1415
)
1516

16-
// HandlerFunc allows you to define a custom function for when a message is stored
17-
type HandlerFunc func(config *config.Config, msg *MessageBody) error
18-
19-
// HandleMessage will stub the handler for processing messages
20-
func (f HandlerFunc) HandleMessage(config *config.Config, msg *MessageBody) error {
21-
return f(config, msg)
22-
}
23-
24-
// Handler allows a custom function to be passed
25-
type Handler interface {
26-
HandleMessage(config *config.Config, msg *MessageBody) error
27-
}
28-
29-
// ParseMessage will take the message attribute and make it readable
30-
func (m *MessageBody) ParseMessage() error {
31-
m.Updatable = false
32-
resp := make(map[string]string)
33-
items := strings.Split(m.Message, "\n")
34-
for _, item := range items {
35-
x := strings.Split(item, "=")
36-
key := x[0]
37-
if key != "" {
38-
s := x[1]
39-
s = s[1 : len(s)-1]
40-
resp[key] = s
41-
}
42-
}
43-
m.ParsedMessage = resp
44-
45-
var resourceProperties ResourceProperties
46-
if resp["ResourceProperties"] != "null" {
47-
err := json.Unmarshal([]byte(resp["ResourceProperties"]), &resourceProperties)
48-
if err != nil {
49-
return err
50-
}
51-
m.ResourceProperties = resourceProperties
52-
for _, tag := range resourceProperties.Tags {
53-
switch tag.Key {
54-
case "Namespace":
55-
m.Namespace = tag.Value
56-
case "ResourceName":
57-
m.ResourceName = tag.Value
58-
}
59-
}
60-
if m.Namespace != "" && m.ResourceName != "" {
61-
m.Updatable = true
62-
}
63-
}
64-
return nil
65-
}
66-
67-
// IsComplete returns a simple status instead of the raw CFT resp
68-
func (m *MessageBody) IsComplete() bool {
69-
return helpers.IsStackComplete(m.ParsedMessage["ResourceStatus"], true)
70-
}
71-
7217
// New will initialize the Queue object for watching
7318
func New(config *config.Config, awsclientset awsclient.ServiceoperatorV1alpha1Interface, timeout int) *Queue {
7419
return &Queue{
@@ -78,21 +23,11 @@ func New(config *config.Config, awsclientset awsclient.ServiceoperatorV1alpha1In
7823
}
7924
}
8025

81-
// Register will create all the affilate resources
82-
func (q *Queue) Register(name string, obj interface{}) (topicARN string, queueURL string, queueAttrs map[string]*string, subARN string) {
83-
config := q.config
84-
logger := config.Logger
85-
keyname := keyName(config.ClusterName, name)
86-
svc := sns.New(config.AWSSession)
87-
topicInputs := sns.CreateTopicInput{Name: aws.String(keyname)}
88-
output, err := svc.CreateTopic(&topicInputs)
89-
if err != nil {
90-
logger.Errorf("Error creating SNS Topic with error '%s'", err.Error())
91-
}
92-
topicARN = *output.TopicArn
93-
logger.Infof("Created sns topic '%s'", topicARN)
94-
26+
// RegisterQueue wkll create the Queue so it is accessible to SNS.
27+
func RegisterQueue(config *config.Config, name string) (queueURL, queueARN string, manager *queuemanager.QueueManager, err error) {
28+
manager = queuemanager.New()
9529
sqsSvc := sqs.New(config.AWSSession)
30+
keyname := keyName(config.ClusterName, name)
9631
queueInputs := sqs.CreateQueueInput{
9732
QueueName: aws.String(keyname),
9833
Attributes: map[string]*string{
@@ -102,14 +37,10 @@ func (q *Queue) Register(name string, obj interface{}) (topicARN string, queueUR
10237
}
10338
sqsOutput, err := sqsSvc.CreateQueue(&queueInputs)
10439
if err != nil {
105-
logger.Errorf("Error creating SQS Queue with error '%s'", err.Error())
40+
return "", "", manager, err
10641
}
10742
queueURL = *sqsOutput.QueueUrl
108-
logger.Infof("Created sqs queue '%s'", queueURL)
109-
q.queueURL = queueURL
11043

111-
// Get Queue Information
112-
// This will later happen with the CRD cft subscription
11344
queueQueryInputs := sqs.GetQueueAttributesInput{
11445
QueueUrl: aws.String(queueURL),
11546
AttributeNames: []*string{
@@ -118,29 +49,46 @@ func (q *Queue) Register(name string, obj interface{}) (topicARN string, queueUR
11849
}
11950
sqsQueueOutput, err := sqsSvc.GetQueueAttributes(&queueQueryInputs)
12051
if err != nil {
121-
logger.Errorf("Error getting SQS Information with error '%s'", err.Error())
52+
return "", "", manager, err
12253
}
123-
queueAttrs = sqsQueueOutput.Attributes
54+
queueARN = *sqsQueueOutput.Attributes["QueueArn"]
55+
return queueURL, queueARN, manager, nil
56+
}
12457

125-
policy := newPolicy(*queueAttrs["QueueArn"], keyname, topicARN)
126-
policyb, err := json.Marshal(policy)
58+
// Subscribe will listen to the global queue and distribute messages
59+
func Subscribe(config *config.Config, manager *queuemanager.QueueManager, ctx context.Context) {
60+
logger := config.Logger
61+
sess, err := session.NewSession(&aws.Config{
62+
Region: aws.String(config.Region),
63+
})
12764
if err != nil {
128-
logger.WithError(err).Error("error encoding policy to json")
65+
logger.WithError(err).Error("error creating AWS session")
66+
os.Exit(1)
12967
}
130-
addPermInput := &sqs.SetQueueAttributesInput{
131-
QueueUrl: aws.String(queueURL),
132-
Attributes: map[string]*string{
133-
"Policy": aws.String(string(policyb)),
134-
}}
135-
_, err = sqsSvc.SetQueueAttributes(addPermInput)
68+
69+
svc := sqs.New(sess)
70+
71+
process(config, svc, manager, ctx)
72+
}
73+
74+
// Register will create all the affilate resources
75+
func (q *Queue) Register(name string) (topicARN string, subARN string) {
76+
config := q.config
77+
logger := config.Logger
78+
keyname := keyName(config.ClusterName, name)
79+
svc := sns.New(config.AWSSession)
80+
topicInputs := sns.CreateTopicInput{Name: aws.String(keyname)}
81+
output, err := svc.CreateTopic(&topicInputs)
13682
if err != nil {
137-
logger.WithError(err).Error("error setting queue policy")
83+
logger.Errorf("Error creating SNS Topic with error '%s'", err.Error())
13884
}
85+
topicARN = *output.TopicArn
86+
logger.Infof("Created sns topic '%s'", topicARN)
13987

14088
// Move to somewhere else.
14189
subInput := sns.SubscribeInput{
14290
TopicArn: aws.String(topicARN),
143-
Endpoint: aws.String(*queueAttrs["QueueArn"]),
91+
Endpoint: aws.String(config.QueueARN),
14492
Protocol: aws.String("sqs"),
14593
}
14694
subOutput, err := svc.Subscribe(&subInput)
@@ -153,84 +101,96 @@ func (q *Queue) Register(name string, obj interface{}) (topicARN string, queueUR
153101
return
154102
}
155103

156-
// StartWatch will start a go routine to watch for new events to store in etcd
157-
func (q *Queue) StartWatch(h Handler, stopCh <-chan struct{}) {
158-
config := q.config
159-
logger := config.Logger
160-
sess, err := session.NewSession(&aws.Config{
161-
Region: aws.String(config.Region),
162-
})
104+
func SetQueuePolicy(config *config.Config, manager *queuemanager.QueueManager) error {
105+
sqsSvc := sqs.New(config.AWSSession)
106+
topicARNs := manager.Keys()
107+
108+
policy := newPolicy(config.QueueARN, config.ClusterName, topicARNs)
109+
policyb, err := json.Marshal(policy)
163110
if err != nil {
164-
logger.WithError(err).Infof("error creating AWS session")
165-
os.Exit(1)
111+
return err
166112
}
167113

168-
svc := sqs.New(sess)
169-
170-
process(q, svc, h, stopCh)
114+
addPermInput := &sqs.SetQueueAttributesInput{
115+
QueueUrl: aws.String(config.QueueURL),
116+
Attributes: map[string]*string{
117+
"Policy": aws.String(string(policyb)),
118+
}}
119+
_, err = sqsSvc.SetQueueAttributes(addPermInput)
120+
if err != nil {
121+
return err
122+
}
123+
return nil
171124
}
172125

173126
func keyName(clusterName string, name string) string {
174127
return clusterName + "-" + name
175128
}
176129

177-
func newPolicy(queueARN string, keyname string, topicARN string) Policy {
178-
return Policy{
179-
Version: "2012-10-17",
180-
ID: queueARN + "/" + keyname + "-policy",
181-
Statement: []Statement{
182-
Statement{
183-
Sid: keyname,
184-
Effect: "Allow",
185-
Principal: "*",
186-
Action: []string{"SQS:ReceiveMessage", "SQS:SendMessage"},
187-
Resource: queueARN,
188-
Condition: Condition{
189-
ArnEquals: ArnEquals{
190-
AwsSourceArn: topicARN,
191-
},
130+
func newPolicy(queueARN string, name string, topicARNs []string) Policy {
131+
statements := []Statement{}
132+
for _, topicARN := range topicARNs {
133+
statements = append(statements, Statement{
134+
Sid: topicARN + " statment",
135+
Effect: "Allow",
136+
Principal: "*",
137+
Action: []string{"SQS:ReceiveMessage", "SQS:SendMessage"},
138+
Resource: queueARN,
139+
Condition: Condition{
140+
ArnEquals: ArnEquals{
141+
AwsSourceArn: topicARN,
192142
},
193143
},
194-
},
144+
})
145+
}
146+
147+
return Policy{
148+
Version: "2012-10-17",
149+
ID: queueARN + "/" + name + "-policy",
150+
Statement: statements,
195151
}
196152
}
197153

198-
func process(q *Queue, svc *sqs.SQS, h Handler, stopCh <-chan struct{}) error {
199-
config := q.config
154+
func process(config *config.Config, svc *sqs.SQS, manager *queuemanager.QueueManager, ctx context.Context) error {
200155
logger := config.Logger
201156
for {
202157
result, err := svc.ReceiveMessage(&sqs.ReceiveMessageInput{
203-
QueueUrl: aws.String(q.queueURL),
158+
QueueUrl: aws.String(config.QueueURL),
204159
AttributeNames: aws.StringSlice([]string{
205160
"SentTimestamp",
206161
}),
207162
MaxNumberOfMessages: aws.Int64(1),
208163
MessageAttributeNames: aws.StringSlice([]string{
209164
"All",
210165
}),
211-
WaitTimeSeconds: aws.Int64(q.timeout),
166+
WaitTimeSeconds: aws.Int64(10),
212167
})
213168
if err != nil {
214169
logger.WithError(err).Error("unable to receive messages from queue")
215170
os.Exit(1)
216171
}
217172

218173
for _, message := range result.Messages {
219-
mb := &MessageBody{}
174+
mb := &queuemanager.MessageBody{}
220175
err := json.Unmarshal([]byte(*message.Body), mb)
221176
if err != nil {
222177
logger.WithError(err).Error("error parsing message body")
223178
}
224179
mb.ParseMessage()
225180
logger.Debugf("%+v", mb)
226181

182+
h, ok := manager.Get(mb.TopicARN)
183+
if !ok {
184+
logger.Errorf("error missing handler function for topic %s", mb.TopicARN)
185+
}
186+
227187
err = h.HandleMessage(config, mb)
228188
if err != nil {
229189
logger.WithError(err).Error("error processing message")
230190
}
231191
logger.Debugf("stackID %v updated status to %v", mb.ParsedMessage["StackId"], mb.ParsedMessage["ResourceStatus"])
232192
_, err = svc.DeleteMessage(&sqs.DeleteMessageInput{
233-
QueueUrl: aws.String(q.queueURL),
193+
QueueUrl: aws.String(config.QueueURL),
234194
ReceiptHandle: message.ReceiptHandle,
235195
})
236196

@@ -239,11 +199,5 @@ func process(q *Queue, svc *sqs.SQS, h Handler, stopCh <-chan struct{}) error {
239199
os.Exit(1)
240200
}
241201
}
242-
243-
select {
244-
case <-stopCh:
245-
os.Exit(1)
246-
default:
247-
}
248202
}
249203
}

pkg/queue/types.go

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -39,26 +39,3 @@ type Condition struct {
3939
type ArnEquals struct {
4040
AwsSourceArn string `json:"aws:SourceArn"`
4141
}
42-
43-
// MessageBody will parse the message from the Body of SQS
44-
type MessageBody struct {
45-
Type string `json:"Type"`
46-
TopicArn string `json:"TopicArn"`
47-
Message string `json:"Message"`
48-
ParsedMessage map[string]string
49-
Namespace string
50-
ResourceName string
51-
ResourceProperties ResourceProperties
52-
Updatable bool
53-
}
54-
55-
// ResourceProperties will wrap the ResourceProperties object
56-
type ResourceProperties struct {
57-
Tags []Tag `json:"Tags"`
58-
}
59-
60-
// Tag represents a Tag
61-
type Tag struct {
62-
Key string `json:"Key"`
63-
Value string `json:"Value"`
64-
}

0 commit comments

Comments
 (0)