Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 44 additions & 5 deletions github/resource_github_enterprise_organization.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,19 @@ import (
"github.com/shurcooL/githubv4"
)

func isSAMLEnforcementError(err error) bool {
if err == nil {
return false
}
var ghErr *github.ErrorResponse
if errors.As(err, &ghErr) {
return ghErr.Response != nil &&
ghErr.Response.StatusCode == 403 &&
strings.Contains(ghErr.Message, "SAML enforcement")
}
return strings.Contains(err.Error(), "Resource protected by organization SAML enforcement")
}

func resourceGithubEnterpriseOrganization() *schema.Resource {
return &schema.Resource{
Create: resourceGithubEnterpriseOrganizationCreate,
Expand Down Expand Up @@ -128,7 +141,17 @@ func resourceGithubEnterpriseOrganizationCreate(data *schema.ResourceData, meta
Name: github.Ptr(displayName),
},
)
return err
if err != nil {
if isSAMLEnforcementError(err) {
// The org was created but we can't set description/display_name until the PAT is authorized.
// Clear them from state so next plan will show drift and retry after PAT authorization.
log.Printf("[WARN] Organization %q created but could not set description/display_name due to SAML enforcement. Authorize the PAT and run apply again.", data.Get("name").(string))
_ = data.Set("description", "")
_ = data.Set("display_name", "")
return nil
}
return err
}
}
return nil
}
Expand Down Expand Up @@ -301,10 +324,18 @@ func updateDescription(ctx context.Context, data *schema.ResourceData, v3 *githu
ctx,
orgName,
&github.Organization{
Description: github.Ptr(data.Get("description").(string)),
Description: github.Ptr(newDesc),
},
)
return err
if err != nil {
if isSAMLEnforcementError(err) {
// Reset state to old value so next plan shows drift
log.Printf("[WARN] Could not update description for %q due to SAML enforcement. Authorize the PAT and run apply again.", orgName)
_ = data.Set("description", oldDesc)
return nil
}
return err
}
}
return nil
}
Expand All @@ -318,10 +349,18 @@ func updateDisplayName(ctx context.Context, data *schema.ResourceData, v4 *githu
ctx,
orgName,
&github.Organization{
Name: github.Ptr(data.Get("display_name").(string)),
Name: github.Ptr(newDisplayName),
},
)
return err
if err != nil {
if isSAMLEnforcementError(err) {
// Reset state to old value so next plan shows drift
log.Printf("[WARN] Could not update display_name for %q due to SAML enforcement. Authorize the PAT and run apply again.", orgName)
_ = data.Set("display_name", oldDisplayName)
return nil
}
return err
}
}
return nil
}
Expand Down
60 changes: 60 additions & 0 deletions github/resource_github_enterprise_organization_test.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,75 @@
package github

import (
"errors"
"fmt"
"net/http"
"regexp"
"strings"
"testing"

"github.com/google/go-github/v81/github"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
)

func TestIsSAMLEnforcementError(t *testing.T) {
tests := []struct {
name string
err error
expected bool
}{
{
name: "nil error",
err: nil,
expected: false,
},
{
name: "GitHub ErrorResponse with SAML enforcement",
err: &github.ErrorResponse{
Response: &http.Response{StatusCode: 403},
Message: "Resource protected by organization SAML enforcement. You must grant your Personal Access token access to this organization.",
},
expected: true,
},
{
name: "GitHub ErrorResponse 403 without SAML message",
err: &github.ErrorResponse{
Response: &http.Response{StatusCode: 403},
Message: "Forbidden",
},
expected: false,
},
{
name: "GitHub ErrorResponse 404",
err: &github.ErrorResponse{
Response: &http.Response{StatusCode: 404},
Message: "Not Found",
},
expected: false,
},
{
name: "plain error with SAML enforcement message",
err: errors.New("Resource protected by organization SAML enforcement"),
expected: true,
},
{
name: "plain error without SAML message",
err: errors.New("some other error"),
expected: false,
},
}

for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
result := isSAMLEnforcementError(tc.err)
if result != tc.expected {
t.Errorf("isSAMLEnforcementError(%v) = %v, want %v", tc.err, result, tc.expected)
}
})
}
}

func TestAccGithubEnterpriseOrganization(t *testing.T) {
t.Run("creates and updates an enterprise organization without error", func(t *testing.T) {
randomID := acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum)
Expand Down