Skip to content

Commit

Permalink
Fixes accidental deletion of Repository resource from state (#1750)
Browse files Browse the repository at this point in the history
Currently when the user does not authenticate the provider, refreshing a GithubRepository resource drops it from the
state, which is unexpected and very confusing.

The root cause of this is: when AnonymousHTTPClient is used, owner == "" and this causes resourceGithubRepositoryRead to
issue requests to non-existent URLs such as https://github.com//myrepo and subsequently interpret 404 as a reason to
drop the resource from the state.

The suggested fix consults the resource data to infer the appropriate owner to use instead of the empty owner.

Co-authored-by: Keegan Campbell <me@kfcampbell.com>
  • Loading branch information
t0yv0 and kfcampbell authored Jun 26, 2023
1 parent b831318 commit 2dfebfe
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 0 deletions.
27 changes: 27 additions & 0 deletions github/resource_github_repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -593,9 +593,16 @@ func resourceGithubRepositoryCreate(d *schema.ResourceData, meta interface{}) er

func resourceGithubRepositoryRead(d *schema.ResourceData, meta interface{}) error {
client := meta.(*Owner).v3client

owner := meta.(*Owner).name
repoName := d.Id()

// When the user has not authenticated the provider, AnonymousHTTPClient is used, therefore owner == "". In this
// case lookup the owner in the data, and use that, if present.
if explicitOwner, _, ok := resourceGithubParseFullName(d); ok && owner == "" {
owner = explicitOwner
}

ctx := context.WithValue(context.Background(), ctxId, d.Id())
if !d.IsNewResource() {
ctx = context.WithValue(ctx, ctxEtag, d.Get("etag").(string))
Expand Down Expand Up @@ -942,3 +949,23 @@ func flattenSecurityAndAnalysis(securityAndAnalysis *github.SecurityAndAnalysis)

return []interface{}{securityAndAnalysisMap}
}

// In case full_name can be determined from the data, parses it into an org and repo name proper. For example,
// resourceGithubParseFullName will return "myorg", "myrepo", true when full_name is "myorg/myrepo".
func resourceGithubParseFullName(resourceDataLike interface {
GetOk(string) (interface{}, bool)
}) (string, string, bool) {
x, ok := resourceDataLike.GetOk("full_name")
if !ok {
return "", "", false
}
s, ok := x.(string)
if !ok || s == "" {
return "", "", false
}
parts := strings.Split(s, "/")
if len(parts) != 2 || parts[0] == "" || parts[1] == "" {
return "", "", false
}
return parts[0], parts[1], true
}
19 changes: 19 additions & 0 deletions github/resource_github_repository_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (

"github.com/hashicorp/terraform-plugin-sdk/helper/acctest"
"github.com/hashicorp/terraform-plugin-sdk/helper/resource"
"github.com/stretchr/testify/assert"
)

func TestAccGithubRepositories(t *testing.T) {
Expand Down Expand Up @@ -1407,3 +1408,21 @@ func reconfigureVisibility(config, visibility string) string {
)
return newConfig
}

type resourceDataLike map[string]interface{}

func (d resourceDataLike) GetOk(key string) (interface{}, bool) {
v, ok := d[key]
return v, ok
}

func TestResourceGithubParseFullName(t *testing.T) {
repo, org, ok := resourceGithubParseFullName(resourceDataLike(map[string]interface{}{"full_name": "myrepo/myorg"}))
assert.True(t, ok)
assert.Equal(t, "myrepo", repo)
assert.Equal(t, "myorg", org)
_, _, ok = resourceGithubParseFullName(resourceDataLike(map[string]interface{}{}))
assert.False(t, ok)
_, _, ok = resourceGithubParseFullName(resourceDataLike(map[string]interface{}{"full_name": "malformed"}))
assert.False(t, ok)
}

0 comments on commit 2dfebfe

Please sign in to comment.