Skip to content

Commit 6864e04

Browse files
committed
contrib/exporters/secadvisor: Encode flows in a JSON object
During the refactoring to pipelines the export format for flows was changed to JSON array, whereas Security Advisor expects the objects in COS/S3 to be a JSON object with a top-level "data" key whose value is an array of flow objects. This change adds an `sa` encode type which adds this top-level element (and uses the core JSON encoder to perform the actual encoding, so `pretty: true` still works). To use this encoder, the secadvisor exporter should have this config: pipeline: encode: type: sa json: pretty: false # or true
1 parent 5ff5f73 commit 6864e04

File tree

6 files changed

+176
-12
lines changed

6 files changed

+176
-12
lines changed

contrib/exporters/allinone/main.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ func main() {
2828
}
2929

3030
func init() {
31+
core.EncoderHandlers.Register("sa", sa.NewEncode, false)
3132
core.TransformerHandlers.Register("awsflowlogs", aws.NewTransform, false)
3233
core.TransformerHandlers.Register("vpclogs", sa.NewTransform, false)
3334
}

contrib/exporters/core/encode.go

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,13 @@ import (
2525
"github.com/spf13/viper"
2626
)
2727

28-
type encodeJSON struct {
28+
// EncodeJSON encoder encodes flows as a JSON array
29+
type EncodeJSON struct {
2930
pretty bool
3031
}
3132

32-
// Encode explements Encounter interface
33-
func (e *encodeJSON) Encode(in interface{}) ([]byte, error) {
33+
// Encode implements Encoder interface
34+
func (e *EncodeJSON) Encode(in interface{}) ([]byte, error) {
3435
buf := new(bytes.Buffer)
3536
encoder := json.NewEncoder(buf)
3637

@@ -45,22 +46,23 @@ func (e *encodeJSON) Encode(in interface{}) ([]byte, error) {
4546
return buf.Bytes(), nil
4647
}
4748

48-
// NewEncodeJSON create an encode object
49+
// NewEncodeJSON creates an encode object
4950
func NewEncodeJSON(cfg *viper.Viper) (interface{}, error) {
50-
return &encodeJSON{
51+
return &EncodeJSON{
5152
pretty: cfg.GetBool(CfgRoot + "encode.json.pretty"),
5253
}, nil
5354
}
5455

55-
type encodeCSV struct {
56+
// EncodeCSV encoder encodes flows as CSV rows
57+
type EncodeCSV struct {
5658
}
5759

58-
// Encode explements Encounter interface
59-
func (e *encodeCSV) Encode(in interface{}) ([]byte, error) {
60+
// Encode implements Encoder interface
61+
func (e *EncodeCSV) Encode(in interface{}) ([]byte, error) {
6062
return gocsv.MarshalBytes(in)
6163
}
6264

63-
// NewEncodeCSV create an encode object
65+
// NewEncodeCSV creates an encode object
6466
func NewEncodeCSV(cfg *viper.Viper) (interface{}, error) {
65-
return &encodeCSV{}, nil
67+
return &EncodeCSV{}, nil
6668
}

contrib/exporters/secadvisor/main.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,5 +27,6 @@ func main() {
2727
}
2828

2929
func init() {
30-
core.TransformerHandlers.Register("sa", mod.NewTransform, false)
30+
core.EncoderHandlers.Register("sa", mod.NewEncode, true)
31+
core.TransformerHandlers.Register("sa", mod.NewTransform, true)
3132
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/*
2+
* Copyright (C) 2019 IBM, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy ofthe License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specificlanguage governing permissions and
14+
* limitations under the License.
15+
*
16+
*/
17+
18+
package mod
19+
20+
import (
21+
"github.com/spf13/viper"
22+
23+
"github.com/skydive-project/skydive/contrib/exporters/core"
24+
)
25+
26+
type encode struct {
27+
*core.EncodeJSON
28+
}
29+
30+
type topLevelObject struct {
31+
Data []interface{} `json:"data"`
32+
}
33+
34+
// Encode the incoming flows array as a JSON object with key "data" whose value
35+
// holds the array
36+
func (e *encode) Encode(in interface{}) ([]byte, error) {
37+
return e.EncodeJSON.Encode(topLevelObject{Data: in.([]interface{})})
38+
}
39+
40+
// NewEncode creates an encode object for Secadvisor format
41+
func NewEncode(cfg *viper.Viper) (interface{}, error) {
42+
jsonEncoder, err := core.NewEncodeJSON(cfg)
43+
if err != nil {
44+
return nil, err
45+
}
46+
return &encode{jsonEncoder.(*core.EncodeJSON)}, nil
47+
}
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
/*
2+
* Copyright (C) 2019 IBM, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy ofthe License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specificlanguage governing permissions and
14+
* limitations under the License.
15+
*
16+
*/
17+
18+
package mod
19+
20+
import (
21+
"encoding/json"
22+
"reflect"
23+
"testing"
24+
25+
"github.com/spf13/viper"
26+
27+
"github.com/skydive-project/skydive/contrib/exporters/core"
28+
)
29+
30+
func areEqualJSON(buf1, buf2 []byte) (bool, error) {
31+
var o1 interface{}
32+
var o2 interface{}
33+
34+
var err error
35+
err = json.Unmarshal(buf1, &o1)
36+
if err != nil {
37+
return false, err
38+
}
39+
err = json.Unmarshal(buf2, &o2)
40+
if err != nil {
41+
return false, err
42+
}
43+
return reflect.DeepEqual(o1, o2), nil
44+
}
45+
46+
func getEncoder(t *testing.T) core.Encoder {
47+
cfg := viper.New()
48+
encoder, err := NewEncode(cfg)
49+
if err != nil {
50+
t.Fatalf("NewEncode returned unexpected error: %v", err)
51+
}
52+
return encoder.(core.Encoder)
53+
}
54+
55+
func Test_Encode_empty_flows_array(t *testing.T) {
56+
in := make([]interface{}, 0)
57+
result, err := getEncoder(t).Encode(in)
58+
if err != nil {
59+
t.Fatalf("Encode returned unexpected error: %v", err)
60+
}
61+
expected := []byte(
62+
`{
63+
"data": []
64+
}`)
65+
equal, err := areEqualJSON(expected, result)
66+
if err != nil {
67+
t.Fatalf("Error parsing JSON: %v", err)
68+
}
69+
if !equal {
70+
t.Fatalf("Objects not identical")
71+
}
72+
}
73+
74+
func Test_Encode_flows_array_with_objects(t *testing.T) {
75+
in := []interface{}{
76+
&SecurityAdvisorFlow{
77+
Status: "STARTED",
78+
Start: 1234,
79+
},
80+
&SecurityAdvisorFlow{
81+
Status: "ENDED",
82+
Last: 5678,
83+
},
84+
}
85+
result, err := getEncoder(t).Encode(in)
86+
if err != nil {
87+
t.Fatalf("Encode returned unexpected error: %v", err)
88+
}
89+
expected := []byte(
90+
`{
91+
"data": [
92+
{
93+
"Status": "STARTED",
94+
"Start": 1234,
95+
"Last": 0,
96+
"UpdateCount": 0
97+
},
98+
{
99+
"Status": "ENDED",
100+
"Start": 0,
101+
"Last": 5678,
102+
"UpdateCount": 0
103+
}
104+
]
105+
}`)
106+
equal, err := areEqualJSON(expected, result)
107+
if err != nil {
108+
t.Fatalf("Error parsing JSON: %v", err)
109+
}
110+
if !equal {
111+
t.Fatalf("Objects not identical")
112+
}
113+
}

contrib/exporters/secadvisor/secadvisor.yml.default

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ pipeline:
3131
sa:
3232
exclude_started_flows: true
3333
encode:
34-
type: json
34+
type: sa
3535
json:
3636
pretty: true
3737
compress:

0 commit comments

Comments
 (0)