diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 00000000..6b6ae2f4 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,31 @@ +name: test +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] +jobs: + ci-test: + runs-on: ubuntu-latest + steps: + - + name: Checkout + uses: actions/checkout@v2 + - + name: Unshallow + run: git fetch --prune --unshallow + - + name: Set up Go + uses: actions/setup-go@v2 + with: + go-version: 1.14 + - + name: fmt check + run: make fmtcheck + + - + name: Test GoReleaser + uses: goreleaser/goreleaser-action@v2 + with: + version: latest + args: build --skip-validate --rm-dist diff --git a/CHANGELOG.md b/CHANGELOG.md index 5c0708fc..99a96f59 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.4.3 (August 14, 2020) + +IMPROVEMENTS: +* **Integration Actions :** add priority for create action (#157) + ## 0.4.2 (August 13, 2020) BUGFIX: diff --git a/GNUmakefile b/GNUmakefile index d3b98888..d03fe7f7 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -6,7 +6,7 @@ PKG_NAME=opsgenie default: build build: fmtcheck - go install + goreleaser build --skip-validate --rm-dist test: fmtcheck go test -i $(TEST) || exit 1 diff --git a/go.mod b/go.mod index fa28532b..0403acdd 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,6 @@ go 1.14 require ( github.com/hashicorp/go-retryablehttp v0.6.6 github.com/hashicorp/terraform-plugin-sdk v1.15.0 - github.com/opsgenie/opsgenie-go-sdk-v2 v1.1.2-0.20200728120710-9ec24baa3544 + github.com/opsgenie/opsgenie-go-sdk-v2 v1.2.1-0.20200814082615-0451657a72bb github.com/pkg/errors v0.8.1 ) diff --git a/go.sum b/go.sum index 977f7586..aaf9493c 100644 --- a/go.sum +++ b/go.sum @@ -15,7 +15,6 @@ github.com/agext/levenshtein v1.2.2/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412/go.mod h1:WPjqKcmVOxf0XSf3YxCJs6N6AOSrOx3obionmG7T0y0= github.com/apparentlymart/go-cidr v1.0.1 h1:NmIwLZ/KdsjIUlhf+/Np40atNXm/+lZ5txfTJ/SpF+U= github.com/apparentlymart/go-cidr v1.0.1/go.mod h1:EBcsNrHc3zQeuaeCeCtQruQm+n9/YjEn/vI25Lg7Gwc= -github.com/apparentlymart/go-dump v0.0.0-20180507223929-23540a00eaa3 h1:ZSTrOEhiM5J5RFxEaFvMZVEAM1KvT1YzbEOwB2EAGjA= github.com/apparentlymart/go-dump v0.0.0-20180507223929-23540a00eaa3/go.mod h1:oL81AME2rN47vu18xqj1S1jPIPuN7afo62yKTNn3XMM= github.com/apparentlymart/go-dump v0.0.0-20190214190832-042adf3cf4a0 h1:MzVXffFUye+ZcSR6opIgz9Co7WcDx6ZcY+RjfFHoA0I= github.com/apparentlymart/go-dump v0.0.0-20190214190832-042adf3cf4a0/go.mod h1:oL81AME2rN47vu18xqj1S1jPIPuN7afo62yKTNn3XMM= @@ -52,7 +51,6 @@ github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFU github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.3.4 h1:87PNWwrRvUSnqS4dlcBU/ftvOIBep4sYuBLlh6rX2wk= @@ -61,7 +59,6 @@ github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEW github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= @@ -142,12 +139,10 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= -github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.1 h1:G1f5SKeVxmagw/IyvzvtZE4Gybcc4Tr1tf7I8z0XgOg= github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= -github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.5 h1:tHXDdz1cpzGaovsTB+TVB8q90WEokoVmfMqoVcrLUgw= github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= @@ -174,8 +169,8 @@ github.com/mitchellh/reflectwalk v1.0.1 h1:FVzMWA5RllMAKIdUSC8mdWo3XtwoecrH79BY7 github.com/mitchellh/reflectwalk v1.0.1/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= -github.com/opsgenie/opsgenie-go-sdk-v2 v1.1.2-0.20200728120710-9ec24baa3544 h1:CcYQ/JYODbXvSyVwc7CDh8OhK0rGMrxcxqJkTvR8KA4= -github.com/opsgenie/opsgenie-go-sdk-v2 v1.1.2-0.20200728120710-9ec24baa3544/go.mod h1:VOkJ7STzYj+nXRhMcBTcmt8uZZ17KZKJdZtJpgHLbT8= +github.com/opsgenie/opsgenie-go-sdk-v2 v1.2.1-0.20200814082615-0451657a72bb h1:jrc1HVw4t8jZxd4LhiRCF9zp+NUsufZ5+KbLZ76ENOg= +github.com/opsgenie/opsgenie-go-sdk-v2 v1.2.1-0.20200814082615-0451657a72bb/go.mod h1:VOkJ7STzYj+nXRhMcBTcmt8uZZ17KZKJdZtJpgHLbT8= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -200,12 +195,10 @@ github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/ulikunitz/xz v0.5.5 h1:pFrO0lVpTBXLpYw+pnLj6TbvHuyjXMfjGeCwSqCVwok= github.com/ulikunitz/xz v0.5.5/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8= -github.com/vmihailenco/msgpack v3.3.3+incompatible h1:wapg9xDUZDzGCNFlwc5SqI1rvcciqcxEHac4CYj89xI= github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= github.com/vmihailenco/msgpack v4.0.1+incompatible h1:RMF1enSPeKTlXrXdOcqjFUElywVZjjC6pqse21bKbEU= github.com/vmihailenco/msgpack v4.0.1+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= github.com/zclconf/go-cty v1.0.0/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s= -github.com/zclconf/go-cty v1.1.0 h1:uJwc9HiBOCpoKIObTQaLR+tsEXx1HBHnOsOOpcdhZgw= github.com/zclconf/go-cty v1.1.0/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s= github.com/zclconf/go-cty v1.2.1 h1:vGMsygfmeCl4Xb6OA5U5XVAaQZ69FvoG7X2jUtQujb8= github.com/zclconf/go-cty v1.2.1/go.mod h1:hOPWgoHbaTUnI5k4D2ld+GRpFJSCe6bCM7m1q/N4PQ8= @@ -264,7 +257,6 @@ golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190502175342-a43fa875dd82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0 h1:HyfiK1WMnHj5FXFXatD+Qs1A/xC2Run6RzeW1SyHxpc= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527 h1:uYVVQ9WP/Ds2ROhcaGPeIdVq0RIXVLwsHlnvJ+cT1So= golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -302,7 +294,6 @@ google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRn google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 h1:gSJIx1SDwno+2ElGhA4+qG2zF97qiUzTM+rQ0klBOcE= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20200310143817-43be25429f5a h1:lRlI5zu6AFy3iU/F8YWyNrAmn/tPCnhiTxfwhWb76eU= google.golang.org/genproto v0.0.0-20200310143817-43be25429f5a/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= @@ -310,7 +301,6 @@ google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEd google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.23.0 h1:AzbTB6ux+okLTzP8Ru1Xs41C303zdcfEht7MQnYJt5A= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.1 h1:zvIju4sqAGvwKspUQOhwnpcqSbzi7/H6QomNNjTL4sk= diff --git a/opsgenie/provider.go b/opsgenie/provider.go index 3971df9f..01688316 100644 --- a/opsgenie/provider.go +++ b/opsgenie/provider.go @@ -26,6 +26,7 @@ func Provider() terraform.ResourceProvider { "opsgenie_team": resourceOpsGenieTeam(), "opsgenie_team_routing_rule": resourceOpsGenieTeamRoutingRule(), + "opsgenie_team_membership": resourceOpsGenieTeamMembership(), "opsgenie_user": resourceOpsGenieUser(), "opsgenie_user_contact": resourceOpsGenieUserContact(), "opsgenie_notification_policy": resourceOpsGenieNotificationPolicy(), diff --git a/opsgenie/resource_opsgenie_integration_action.go b/opsgenie/resource_opsgenie_integration_action.go index fb1cc354..c37fbdb9 100644 --- a/opsgenie/resource_opsgenie_integration_action.go +++ b/opsgenie/resource_opsgenie_integration_action.go @@ -44,6 +44,10 @@ func resourceOpsgenieIntegrationAction() *schema.Resource { Optional: true, Default: 1, }, + "priority": { + Type: schema.TypeString, + Optional: true, + }, "filter": { Type: schema.TypeList, Optional: true, @@ -513,6 +517,9 @@ func expandOpsgenieIntegrationActions(input interface{}) []integration.Integrati action.Order = inputMap["order"].(int) action.User = inputMap["user"].(string) action.Note = inputMap["note"].(string) + if priority := inputMap["priority"]; priority != nil { + action.Priority = priority.(string) + } filters := expandOpsgenieFilter(inputMap["filter"].([]interface{})) action.Filter = &filters @@ -593,6 +600,7 @@ func flattenOpsgenieIntegrationActions(input []integration.IntegrationAction) [] actionMap["filter"] = flattenOpsgenieFilter(action.Filter) if action.Type == "create" { actionMap["source"] = action.Source + actionMap["priority"] = action.Priority actionMap["message"] = action.Message actionMap["description"] = action.Description actionMap["entity"] = action.Entity diff --git a/opsgenie/resource_opsgenie_integration_action_test.go b/opsgenie/resource_opsgenie_integration_action_test.go index 6a8d347d..08f124a0 100644 --- a/opsgenie/resource_opsgenie_integration_action_test.go +++ b/opsgenie/resource_opsgenie_integration_action_test.go @@ -207,6 +207,7 @@ resource "opsgenie_integration_action" "test_api" { message = "{{message}}" description = "{{description}}" entity = "{{entity}}" + priority = "P2" alert_actions = ["Check error rate"] extra_properties = map( "Environment", "test-env", diff --git a/opsgenie/resource_opsgenie_team_membership.go b/opsgenie/resource_opsgenie_team_membership.go new file mode 100644 index 00000000..4bf5202a --- /dev/null +++ b/opsgenie/resource_opsgenie_team_membership.go @@ -0,0 +1,159 @@ +package opsgenie + +import ( + "context" + "fmt" + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + "github.com/opsgenie/opsgenie-go-sdk-v2/team" + "log" + "strings" +) + +func resourceOpsGenieTeamMembership() *schema.Resource { //TODO encode the e-mail addrs (e.g. because of +)? https://github.com/opsgenie/opsgenie-go-sdk-v2/issues/62 + return &schema.Resource{ + Create: resourceOpsGenieTeamMembershipCreate, + Read: handleNonExistentResource(resourceOpsGenieTeamMembershipRead), + //Update: resourceOpsGenieTeamMembershipUpdate, // requires https://github.com/opsgenie/opsgenie-go-sdk-v2/issues/59 + Delete: resourceOpsGenieTeamMembershipDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + Schema: map[string]*schema.Schema{ + "user_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "role": { + Type: schema.TypeString, + Optional: true, + Default: "user", + ForceNew: true, + }, + "team_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + }, + } +} + +func resourceOpsGenieTeamMembershipCreate(d *schema.ResourceData, meta interface{}) error { + + userID := d.Get("user_id").(string) + role := d.Get("role").(string) + teamID := d.Get("team_id").(string) + + log.Printf("[INFO] Adding user %q to team %q", teamID, userID) + + client, err := team.NewClient(meta.(*OpsgenieClient).client.Config) + if err != nil { + return err + } + + // add member to team + _, err = client.AddMember(context.Background(), &team.AddTeamMemberRequest{ + TeamIdentifierType: team.Id, + TeamIdentifierValue: teamID, + User: team.User{ + ID: userID, + }, + Role: role, + }) + if err != nil { + return err + } + + d.SetId(buildTwoPartID(teamID, userID)) + + return resourceOpsGenieTeamMembershipRead(d, meta) +} + +func resourceOpsGenieTeamMembershipRead(d *schema.ResourceData, meta interface{}) error { + + teamID, userID, err := parseTwoPartID(d.Id(), "teamID", "userID") + if err != nil { + return err + } + + getRequest := &team.GetTeamRequest{ + IdentifierType: team.Id, + IdentifierValue: teamID, + } + + log.Printf("[INFO] Retrieving membership of user %q in team %q", userID, teamID) + + client, err := team.NewClient(meta.(*OpsgenieClient).client.Config) + if err != nil { + return err + } + + getResponse, err := client.Get(context.Background(), getRequest) + if err != nil { + return err + } + + role, err := getUserRole(userID, teamID, getResponse.Members) + if err != nil { + return err + } + + d.Set("user_id", userID) + d.Set("role", role) + d.Set("team_id", teamID) + + return nil +} + +func resourceOpsGenieTeamMembershipDelete(d *schema.ResourceData, meta interface{}) error { + userID := d.Get("user_id").(string) + teamID := d.Get("team_id").(string) + + log.Printf("[INFO] Deleting membership of user %q in team %q", userID, teamID) + + client, err := team.NewClient(meta.(*OpsgenieClient).client.Config) + if err != nil { + return err + } + + _, err = client.RemoveMember(context.Background(), &team.RemoveTeamMemberRequest{ + TeamIdentifierType: team.Id, + TeamIdentifierValue: teamID, + MemberIdentifierType: team.Id, + MemberIdentifierValue: userID, + }) + if err != nil { + return err + } + + return nil +} + +func getUserRole(userID string, teamID string, input []team.Member) (string, error) { + role := "" + + for _, inputMember := range input { + if inputMember.User.ID == userID { + role = inputMember.Role + return role, nil + } + } + + return "", fmt.Errorf("did not found user %q in team %q (%#v)", userID, teamID, input) +} + +// format the strings into an id `a:b` +func buildTwoPartID(a, b string) string { + return fmt.Sprintf("%s:%s", a, b) +} + +// return the pieces of id `left:right` as left, right +func parseTwoPartID(id, left, right string) (string, string, error) { + parts := strings.SplitN(id, ":", 2) + if len(parts) != 2 { + return "", "", fmt.Errorf("unexpected ID format %q, expected %s:%s", id, left, right) + } + + return parts[0], parts[1], nil +} diff --git a/opsgenie/resource_opsgenie_team_membershop_test.go b/opsgenie/resource_opsgenie_team_membershop_test.go new file mode 100644 index 00000000..cf2fd4f8 --- /dev/null +++ b/opsgenie/resource_opsgenie_team_membershop_test.go @@ -0,0 +1,194 @@ +package opsgenie + +import ( + "context" + "fmt" + "github.com/hashicorp/terraform-plugin-sdk/terraform" + "github.com/opsgenie/opsgenie-go-sdk-v2/team" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" +) + +func TestAccOpsGenieTeamMembership_basic(t *testing.T) { + rString := acctest.RandString(6) + + resource.Test(t, resource.TestCase{ + Providers: testAccProviders, + + Steps: []resource.TestStep{ + { + Config: testAccOpsGenieTeamMembership_basic(rString), + Destroy: false, + Check: resource.ComposeTestCheckFunc( + testCheckOpsGenieTeamExists("opsgenie_team.monkeys"), + testCheckOpsGenieUserExists("opsgenie_user.kong"), + testCheckOpsGenieTeamMembershipExists("opsgenie_team_membership.chaos_kong", "opsgenie_team.monkeys", "opsgenie_user.kong"), + ), + }, + { + Config: testAccOpsGenieTeamMembership_basicUpdated(rString), + Check: resource.ComposeTestCheckFunc( + testCheckOpsGenieTeamExists("opsgenie_team.monkeys"), + testCheckOpsGenieUserExists("opsgenie_user.kong"), + testCheckOpsGenieTeamMembershipExists("opsgenie_team_membership.chaos_kong", "opsgenie_team.monkeys", "opsgenie_user.kong"), + ), + }, + { + Config: testAccOpsGenieTeamMembership_basicWithoutMembership(rString), + Check: resource.ComposeTestCheckFunc( + testCheckOpsGenieTeamExists("opsgenie_team.monkeys"), + testCheckOpsGenieUserExists("opsgenie_user.kong"), + testCheckOpsGenieTeamMembershipRemoved("opsgenie_team_membership.chaos_kong", "opsgenie_team.monkeys", "opsgenie_user.kong"), + ), + }, + }, + }) +} + +func testCheckOpsGenieTeamMembershipExists(membershipResource string, teamResource string, userResource string) resource.TestCheckFunc { + return func(s *terraform.State) error { + + rsMembership, ok := s.RootModule().Resources[membershipResource] + if !ok { + return fmt.Errorf("not found: %s", membershipResource) + } + + rsTeam, ok := s.RootModule().Resources[teamResource] + if !ok { + return fmt.Errorf("not found: %s", teamResource) + } + teamName := rsTeam.Primary.Attributes["name"] + + rsUser, ok := s.RootModule().Resources[userResource] + if !ok { + return fmt.Errorf("not found: %s", userResource) + } + userName := rsUser.Primary.Attributes["username"] + + client, err := team.NewClient(testAccProvider.Meta().(*OpsgenieClient).client.Config) + if err != nil { + return err + } + req := team.GetTeamRequest{ + IdentifierType: team.Name, + IdentifierValue: teamName, + } + getResponse, err := client.Get(context.Background(), &req) + if err != nil { + return fmt.Errorf("failed to detect team membership for user %q in team %q: %s", userName, teamName, err) + } + + // compare what we've actually done + if len(getResponse.Members) != 1 { + return fmt.Errorf("there's no team membership at all. something went wrong :(") + } + + if getResponse.Members[0].User.Username != userName { + return fmt.Errorf("expected userName in team membership (%q) doesn't match actual username (%q)", userName, getResponse.Members[0].User.Username) + } + + if getResponse.Members[0].User.ID != rsUser.Primary.ID { + return fmt.Errorf("expected user ID in team membership (%q) doesn't match actual username (%q)", rsUser.Primary.ID, getResponse.Members[0].User.ID) + } + + if getResponse.Members[0].Role != rsMembership.Primary.Attributes["role"] { + return fmt.Errorf("expected user role in team membership (%q) doesn't match actual user role (%q)", rsMembership.Primary.Attributes["role"], getResponse.Members[0].Role) + } + + return nil + } +} + +func testCheckOpsGenieTeamMembershipRemoved(membershipResource string, teamResource string, userResource string) resource.TestCheckFunc { + return func(s *terraform.State) error { + + _, ok := s.RootModule().Resources[membershipResource] + if ok { + return fmt.Errorf("resource %s still in state. this is bad", membershipResource) + } + + rsTeam, ok := s.RootModule().Resources[teamResource] + if !ok { + return fmt.Errorf("not found: %s", teamResource) + } + teamName := rsTeam.Primary.Attributes["name"] + + client, err := team.NewClient(testAccProvider.Meta().(*OpsgenieClient).client.Config) + if err != nil { + return err + } + req := team.GetTeamRequest{ + IdentifierType: team.Name, + IdentifierValue: teamName, + } + getResponse, err := client.Get(context.Background(), &req) + if err != nil { + return fmt.Errorf("failed to verify team memberships of team %q: %s", teamName, err) + } + + // compare what we've actually done + if len(getResponse.Members) != 0 { + return fmt.Errorf("there is still an unexpected number of team membership(s) (%#v). something went wrong :(", getResponse.Members) + } + + return nil + } +} + +func testAccOpsGenieTeamMembership_basic(rString string) string { + return fmt.Sprintf(` +resource "opsgenie_team" "monkeys" { + name = "monkeys-%s" + description = "They exist." + ignore_members = true +} +resource "opsgenie_user" "kong" { + username = "kong-%s@test.example.com" + full_name = "Chaos Kong" + role = "User" +} +resource "opsgenie_team_membership" "chaos_kong" { + username = opsgenie_user.kong.username + role = "user" + team = opsgenie_team.monkeys.name +} +`, rString, rString) +} + +func testAccOpsGenieTeamMembership_basicUpdated(rString string) string { + return fmt.Sprintf(` +resource "opsgenie_team" "monkeys" { + name = "monkeys-%s" + description = "They exist." + ignore_members = true +} +resource "opsgenie_user" "kong" { + username = "kong-%s@test.example.com" + full_name = "Chaos Kong" + role = "User" +} +resource "opsgenie_team_membership" "chaos_kong" { + username = opsgenie_user.kong.username + role = "admin" + team = opsgenie_team.monkeys.name +} +`, rString, rString) +} + +func testAccOpsGenieTeamMembership_basicWithoutMembership(rString string) string { + return fmt.Sprintf(` +resource "opsgenie_team" "monkeys" { + name = "monkeys-%s" + description = "They exist." + ignore_members = true + depends_on = [opsgenie_user.kong] # Just a hack for the test to destroy resources in the right order +} +resource "opsgenie_user" "kong" { + username = "kong-%s@test.example.com" + full_name = "Chaos Kong" + role = "User" +} +`, rString, rString) +} diff --git a/opsgenie/resource_opsgenie_user.go b/opsgenie/resource_opsgenie_user.go index 3ffaeb5f..2e7b9667 100644 --- a/opsgenie/resource_opsgenie_user.go +++ b/opsgenie/resource_opsgenie_user.go @@ -53,6 +53,8 @@ func resourceOpsGenieUser() *schema.Resource { } func resourceOpsGenieUserCreate(d *schema.ResourceData, meta interface{}) error { + //TODO OGS-1629: Atlassian systems reset full_name after a certain time after creating a new OpsGenie user. + // This may lead to unexpected behaviour, e.g. when running a subsequent "tf apply" or executing our acceptance tests client, err := user.NewClient(meta.(*OpsgenieClient).client.Config) if err != nil { diff --git a/opsgenie/resource_opsgenie_user_test.go b/opsgenie/resource_opsgenie_user_test.go index 92a6e96b..26a6b8a5 100644 --- a/opsgenie/resource_opsgenie_user_test.go +++ b/opsgenie/resource_opsgenie_user_test.go @@ -154,7 +154,7 @@ func testCheckOpsGenieUserExists(name string) resource.TestCheckFunc { if err != nil { return fmt.Errorf("Bad: User %q (username: %q) does not exist", id, username) } else { - log.Printf("User found :%s ", result.Username) + log.Printf("User found: %s", result.Username) } return nil diff --git a/vendor/github.com/opsgenie/opsgenie-go-sdk-v2/integration/request.go b/vendor/github.com/opsgenie/opsgenie-go-sdk-v2/integration/request.go index f419169c..5aef775a 100644 --- a/vendor/github.com/opsgenie/opsgenie-go-sdk-v2/integration/request.go +++ b/vendor/github.com/opsgenie/opsgenie-go-sdk-v2/integration/request.go @@ -257,8 +257,8 @@ func (r *GetIntegrationActionsRequest) Method() string { } type Filter struct { - ConditionMatchType og.ConditionMatchType `json:"conditionMatchType,omitempty"` - Conditions []og.Condition `json:"conditions,omitempty"` + ConditionMatchType og.ConditionMatchType `json:"conditionMatchType,omitempty"` + Conditions []og.Condition `json:"conditions,omitempty"` } type CreateIntegrationActionsRequest struct { @@ -339,6 +339,7 @@ type IntegrationAction struct { Message string `json:"message,omitempty"` Description string `json:"description,omitempty"` Entity string `json:"entity,omitempty"` + Priority string `json:"priority,omitempty"` AppendAttachments *bool `json:"appendAttachments,omitempty"` AlertActions []string `json:"alertActions,omitempty"` IgnoreAlertActionsFromPayload *bool `json:"ignoreAlertActionsFromPayload,omitempty"` diff --git a/vendor/modules.txt b/vendor/modules.txt index b1f6073a..ff32a28f 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -215,7 +215,7 @@ github.com/mitchellh/mapstructure github.com/mitchellh/reflectwalk # github.com/oklog/run v1.0.0 github.com/oklog/run -# github.com/opsgenie/opsgenie-go-sdk-v2 v1.1.2-0.20200728120710-9ec24baa3544 +# github.com/opsgenie/opsgenie-go-sdk-v2 v1.2.1-0.20200814082615-0451657a72bb ## explicit github.com/opsgenie/opsgenie-go-sdk-v2/alert github.com/opsgenie/opsgenie-go-sdk-v2/client diff --git a/website/docs/r/integration_action.markdown b/website/docs/r/integration_action.markdown index 5ae11a3f..9b8e68f5 100644 --- a/website/docs/r/integration_action.markdown +++ b/website/docs/r/integration_action.markdown @@ -54,6 +54,7 @@ resource "opsgenie_integration_action" "test_action" { create { name = "Create medium priority alerts" tags = ["SEVERE", "SEV-1"] + priority = "P3" filter { type = "match-all-conditions" conditions { @@ -134,6 +135,8 @@ The following arguments are common and supported for all actions: * `entity` - (Optional) The entity the alert is related to. +* `priority` - (Optional) Alert priority. + * `extra_properties` - (Optional) Set of user defined properties specified as a map. * `message` - (Optional) Alert text limited to 130 characters. diff --git a/website/docs/r/team_membership.html.markdown b/website/docs/r/team_membership.html.markdown new file mode 100644 index 00000000..525f8e93 --- /dev/null +++ b/website/docs/r/team_membership.html.markdown @@ -0,0 +1,64 @@ +--- +layout: "opsgenie" +page_title: "Opsgenie: opsgenie_team_membership" +sidebar_current: "docs-opsgenie-resource-team-membership" +description: |- + Manages team memberships for users. +--- + +# opsgenie\_team\_membership + +Manages team memberships for users. + +## Example Usage + +```hcl +resource "opsgenie_user" "first" { + username = "user@test.example.com" + full_name = "name " + role = "User" +} + +resource "opsgenie_user" "second" { + username = "test@test.example.com" + full_name = "name " + role = "User" +} + +resource "opsgenie_team" "test" { + name = "example" + description = "This team deals with all the things" + ignore_members = true # we're using opsgenie_team_membership for it +} + +resource "opsgenie_team_membership" "first" { + user_id = opsgenie_user.first.id + team_id = opsgenie_team.test.id +} + +resource "opsgenie_team_membership" "second" { + user_id = opsgenie_user.second.id + role = "admin" + team_id = opsgenie_team.test.id +} +``` + +## Argument Reference + +The following arguments are supported: + +* `user_id` - (Required) The ID of an user. + +* `role` - (Optional) The role for the user within the Team - can be either 'admin' or 'user', defaults to 'user' if not set. + +* `team_id` - (Required) The ID of a team the user should be member of. + +## Attributes Reference + +The following attributes are exported: + +* `id` - The virtual ID of the Opsgenie Team Membership. + +## Import + +Import is not supported for team memberships. \ No newline at end of file diff --git a/website/opsgenie.erb b/website/opsgenie.erb index 281544d7..b16d8f92 100644 --- a/website/opsgenie.erb +++ b/website/opsgenie.erb @@ -44,6 +44,9 @@