diff --git a/sysdig/provider.go b/sysdig/provider.go index e5f863d7..bf498725 100644 --- a/sysdig/provider.go +++ b/sysdig/provider.go @@ -47,6 +47,7 @@ func Provider() terraform.ResourceProvider { "sysdig_secure_rule_falco": resourceSysdigSecureRuleFalco(), "sysdig_secure_team": resourceSysdigSecureTeam(), "sysdig_secure_list": resourceSysdigSecureList(), + "sysdig_secure_macro": resourceSysdigSecureMacro(), "sysdig_monitor_alert_downtime": resourceSysdigMonitorAlertDowntime(), "sysdig_monitor_alert_metric": resourceSysdigMonitorAlertMetric(), diff --git a/sysdig/resource_sysdig_secure_list.go b/sysdig/resource_sysdig_secure_list.go index 21f7b07d..53a6b5c1 100644 --- a/sysdig/resource_sysdig_secure_list.go +++ b/sysdig/resource_sysdig_secure_list.go @@ -40,6 +40,7 @@ func resourceSysdigSecureList() *schema.Resource { "append": { Type: schema.TypeBool, Optional: true, + Default: false, }, "version": { Type: schema.TypeInt, diff --git a/sysdig/resource_sysdig_secure_macro.go b/sysdig/resource_sysdig_secure_macro.go new file mode 100644 index 00000000..fb2668c9 --- /dev/null +++ b/sysdig/resource_sysdig_secure_macro.go @@ -0,0 +1,109 @@ +package sysdig + +import ( + "github.com/draios/terraform-provider-sysdig/sysdig/secure" + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + "strconv" + "time" +) + +func resourceSysdigSecureMacro() *schema.Resource { + timeout := 30 * time.Second + + return &schema.Resource{ + Create: resourceSysdigMacroCreate, + Update: resourceSysdigMacroUpdate, + Read: resourceSysdigMacroRead, + Delete: resourceSysdigMacroDelete, + + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(timeout), + Update: schema.DefaultTimeout(timeout), + Read: schema.DefaultTimeout(timeout), + Delete: schema.DefaultTimeout(timeout), + }, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "append": { + Type: schema.TypeBool, + Optional: true, + Default: false, + }, + "condition": { + Type: schema.TypeString, + Required: true, + }, + "version": { + Type: schema.TypeInt, + Computed: true, + }, + }, + } +} + +func resourceSysdigMacroCreate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*SysdigClients).sysdigSecureClient + + macro := macroFromResourceData(d) + macro, err := client.CreateMacro(macro) + if err != nil { + return err + } + + d.SetId(strconv.Itoa(macro.ID)) + d.Set("version", macro.Version) + + return nil +} + +func resourceSysdigMacroUpdate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*SysdigClients).sysdigSecureClient + + macro := macroFromResourceData(d) + macro.Version = d.Get("version").(int) + + id, _ := strconv.Atoi(d.Id()) + macro.ID = id + + _, err := client.UpdateMacro(macro) + return err +} + +func resourceSysdigMacroRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*SysdigClients).sysdigSecureClient + + id, _ := strconv.Atoi(d.Id()) + macro, err := client.GetMacroById(id) + + if err != nil { + d.SetId("") + } + + d.Set("name", macro.Name) + d.Set("version", macro.Version) + d.Set("items", macro.Condition.Condition) + d.Set("append", macro.Append) + + return nil +} + +func resourceSysdigMacroDelete(d *schema.ResourceData, meta interface{}) error { + client := meta.(*SysdigClients).sysdigSecureClient + + id, _ := strconv.Atoi(d.Id()) + + return client.DeleteMacro(id) +} + +func macroFromResourceData(d *schema.ResourceData) secure.Macro { + return secure.Macro{ + Name: d.Get("name").(string), + Append: d.Get("append").(bool), + Condition: secure.MacroCondition{Condition: d.Get("condition").(string)}, + } +} diff --git a/sysdig/resource_sysdig_secure_macro_test.go b/sysdig/resource_sysdig_secure_macro_test.go new file mode 100644 index 00000000..ff4892d3 --- /dev/null +++ b/sysdig/resource_sysdig_secure_macro_test.go @@ -0,0 +1,105 @@ +package sysdig_test + +import ( + "fmt" + "github.com/draios/terraform-provider-sysdig/sysdig" + "github.com/hashicorp/terraform-plugin-sdk/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/terraform" + "os" + "testing" +) + +func TestAccMacro(t *testing.T) { + rText := func() string { return acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum) } + fixedRandomText := rText() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + if v := os.Getenv("SYSDIG_SECURE_API_TOKEN"); v == "" { + t.Fatal("SYSDIG_SECURE_API_TOKEN must be set for acceptance tests") + } + }, + Providers: map[string]terraform.ResourceProvider{ + "sysdig": sysdig.Provider(), + }, + Steps: []resource.TestStep{ + { + Config: macroWithName(rText()), + }, + { + Config: macroWithName(fixedRandomText), + }, + { + Config: macroUpdatedWithName(fixedRandomText), + }, + { + Config: macroAppendToDefault(), + }, + { + Config: macroWithMacro(rText(), rText()), + }, + { + Config: macroWithMacroAndList(rText(), rText(), rText()), + }, + }, + }) +} + +func macroWithName(name string) string { + return fmt.Sprintf(` +resource "sysdig_secure_macro" "sample" { + name = "terraform_test_%s" + condition = "always_true" +} +`, name) +} + +func macroUpdatedWithName(name string) string { + return fmt.Sprintf(` +resource "sysdig_secure_macro" "sample" { + name = "terraform_test_%s" + condition = "never_true" +} +`, name) +} + +func macroAppendToDefault() string { + return fmt.Sprintf(` +resource "sysdig_secure_macro" "sample2" { + name = "container" + condition = "and always_true" + append = true +} +`) +} + +func macroWithMacro(name1, name2 string) string { + return fmt.Sprintf(` +resource "sysdig_secure_macro" "sample3" { + name = "terraform_test_%s" + condition = "always_true" +} + +resource "sysdig_secure_macro" "sample4" { + name = "terraform_test_%s" + condition = "never_true and ${sysdig_secure_macro.sample3.name}" +} +`, name1, name2) +} + +func macroWithMacroAndList(name1, name2, name3 string) string { + return fmt.Sprintf(` +%s + +resource "sysdig_secure_macro" "sample5" { + name = "terraform_test_%s" + condition = "fd.name in (${sysdig_secure_list.sample.name})" +} + +resource "sysdig_secure_macro" "sample6" { + name = "terraform_test_%s" + condition = "never_true and ${sysdig_secure_macro.sample5.name}" +} +`, listWithName(name3), name1, name2) +} diff --git a/sysdig/secure/client.go b/sysdig/secure/client.go index c144f415..cec55b3b 100644 --- a/sysdig/secure/client.go +++ b/sysdig/secure/client.go @@ -38,6 +38,11 @@ type SysdigSecureClient interface { GetListById(int) (List, error) DeleteList(int) error UpdateList(List) (List, error) + + CreateMacro(Macro) (Macro, error) + GetMacroById(int) (Macro, error) + DeleteMacro(int) error + UpdateMacro(Macro) (Macro, error) } func NewSysdigSecureClient(sysdigSecureAPIToken string, url string) SysdigSecureClient { diff --git a/sysdig/secure/macros.go b/sysdig/secure/macros.go new file mode 100644 index 00000000..6e020cd7 --- /dev/null +++ b/sysdig/secure/macros.go @@ -0,0 +1,90 @@ +package secure + +import ( + "errors" + "fmt" + "io/ioutil" + "net/http" +) + +func (client *sysdigSecureClient) CreateMacro(macroRequest Macro) (macro Macro, err error) { + response, err := client.doSysdigSecureRequest(http.MethodPost, client.GetMacrosUrl(), macroRequest.ToJSON()) + if err != nil { + return + } + defer response.Body.Close() + + body, _ := ioutil.ReadAll(response.Body) + + if response.StatusCode != http.StatusOK && response.StatusCode != http.StatusCreated { + err = errors.New(response.Status) + return + } + + macro, err = MacroFromJSON(body) + return +} + +func (client *sysdigSecureClient) GetMacroById(id int) (macro Macro, err error) { + response, err := client.doSysdigSecureRequest(http.MethodGet, client.GetMacroUrl(id), nil) + if err != nil { + return + } + defer response.Body.Close() + + body, _ := ioutil.ReadAll(response.Body) + + if response.StatusCode != http.StatusOK { + err = errors.New(response.Status) + return + } + + macro, err = MacroFromJSON(body) + if err != nil { + return + } + + if macro.Version == 0 { + err = fmt.Errorf("Macro with ID: %d does not exists", id) + return + } + return +} + +func (client *sysdigSecureClient) UpdateMacro(macroRequest Macro) (macro Macro, err error) { + response, err := client.doSysdigSecureRequest(http.MethodPut, client.GetMacroUrl(macroRequest.ID), macroRequest.ToJSON()) + if err != nil { + return + } + defer response.Body.Close() + + body, _ := ioutil.ReadAll(response.Body) + + if response.StatusCode != http.StatusOK { + err = errors.New(response.Status) + return + } + + return MacroFromJSON(body) +} + +func (client *sysdigSecureClient) DeleteMacro(id int) error { + response, err := client.doSysdigSecureRequest(http.MethodDelete, client.GetMacroUrl(id), nil) + if err != nil { + return err + } + defer response.Body.Close() + + if response.StatusCode != http.StatusNoContent && response.StatusCode != http.StatusOK { + return errors.New(response.Status) + } + return nil +} + +func (client *sysdigSecureClient) GetMacrosUrl() string { + return fmt.Sprintf("%s/api/secure/falco/macros", client.URL) +} + +func (client *sysdigSecureClient) GetMacroUrl(id int) string { + return fmt.Sprintf("%s/api/secure/falco/macros/%d", client.URL, id) +} diff --git a/sysdig/secure/models.go b/sysdig/secure/models.go index c4788681..ac440094 100644 --- a/sysdig/secure/models.go +++ b/sysdig/secure/models.go @@ -229,6 +229,30 @@ func ListFromJSON(body []byte) (list List, err error) { return } +// -------- Macro ------- + +type Macro struct { + ID int `json:"id,omitempty"` + Version int `json:"version,omitempty"` + Name string `json:"name"` + Condition MacroCondition `json:"condition"` + Append bool `json:"append"` +} + +type MacroCondition struct { + Condition string `json:"condition"` +} + +func (l *Macro) ToJSON() io.Reader { + payload, _ := json.Marshal(l) + return bytes.NewBuffer(payload) +} + +func MacroFromJSON(body []byte) (macro Macro, err error) { + err = json.Unmarshal(body, ¯o) + return +} + // -------- User -------- type User struct { ID int `json:"id,omitempty"`