terraform-provider-github icon indicating copy to clipboard operation
terraform-provider-github copied to clipboard

Deleting/Destroying an archived repo results in 403 error.

Open BlackDex opened this issue 3 years ago • 4 comments

Hello,

I'm having issues trying to remove/destroy github repo's by either using terraform destroy or after removing a module from the code and doing a terraform plan on repo's which are archived. I have several archived repo's which i want to delete, but that isn't possible using terraform only right now when the archived flag is set to true. It complains about some resources that arn't able to be modified because the archive is in read-only state.

Terraform Version

Terraform v1.0.3
on linux_amd64
+ provider registry.terraform.io/gitlabhq/gitlab v3.7.0
+ provider registry.terraform.io/hashicorp/aws v3.52.0
+ provider registry.terraform.io/hashicorp/null v3.1.0
+ provider registry.terraform.io/integrations/github v4.13.0

Affected Resource(s)

At least the following, could be more:

  • github_repository_collaborator
  • github_repository

Terraform Configuration Files (as a module)

resource "github_repository" "repository" {
  name                   = var.name
  description            = var.description
  visibility             = var.visibility
  has_issues             = var.issues
  has_wiki               = false
  auto_init              = var.auto_init
  allow_squash_merge     = var.allow_squash_merge
  allow_rebase_merge     = var.allow_rebase_merge
  allow_merge_commit     = var.allow_merge_commit
  delete_branch_on_merge = var.delete_branch_on_merge
  topics                 = var.topics
  archived               = var.archived
}

resource "github_repository_collaborator" "cicd-deployer_ro" {
  count      = var.allow_cicd-deployer_to_push == "true" ? 0 : 1
  repository = github_repository.repository.name
  username   = "cicd-deployer"
  permission = "pull"
}

resource "github_repository_collaborator" "cicd-deployer_rw" {
  count      = var.allow_cicd-deployer_to_push == "true" ? 1 : 0
  repository = github_repository.repository.name
  username   = "cicd-deployer"
  permission = "push"
}

Debug Output

Not able to provide actual debug output here, but the following line should be enough. Error: DELETE https://api.github.com/repos/OWNER/REPO/collaborators/cicd-deployer: 403 Repository was archived so is read-only. []

Panic Output

n/a

Expected Behavior

Repository is deleted from github and nicely cleaned from terraform state.

Actual Behavior

The github provider returns an error that a specific resource is not being able to be deleted because the repository is set to read-only. If i manually delete the repo via github api call it works fine and all linked resources are removed by github.

Steps to Reproduce

Please list the steps required to reproduce the issue, for example:

  1. Use something like the above provided code.
  • Have a github_repository and a github_repository_collaborator both configured.
  1. terraform apply
  2. Configure that repo to be archived via the archived flag.
  3. terraform apply
  4. Either comment out that repo or use terraform destroy to remove that module.
  5. It will error out because the repo is read-only and removing collaborators isn't possible.

Important Factoids

Non as far is a know, it happens at least on private repo's since that is where this is configured on.

References

n/a

BlackDex avatar Aug 11 '21 12:08 BlackDex

While this is technically a bug, this appears to be a limitation of the way Terraform handles dependencies, mixed with the particular strategy this provider uses to handle different aspects of a repository's configuration, and a deficiency in the GitHub API.

The collaborator resources (in your configuration) "depend on" the repository resource. This means that they MUST be deleted before removing the repository. However, they cannot be deleted while the repository is archived. Also, the GitHub API does not support unarchiving a repository.

Even if it did, this problem would still partially exist. The collaborator resources should not override the repository's archived setting, so you would have to terraform apply the repository resource with archived = false BEFORE applying a plan that destroys the resources.

There are really no good solutions to this at the moment, and there will likely never be a non-hacky solution that doesn't require two apply/destroy commands. Aside from just deleting it in GitHub, the best I can offer you is to do this when you want to delete an archived repo:

  1. Adjust any resources that depend on the repository to not depend on the repository (in this case, change collaborator's repository to be var.name
  2. Comment out/remove the repository
  3. terraform apply
  4. Comment out/remove the collaborator
  5. terraform apply

There is a also way to hack together a slightly more automated solution using some method to store state outside of terraform (or some hackery with terraform cloud). It's a pain and really, really, really gross, so I won't type it up unless you ask me to. It still isnt a one step solution for deletion, but it is only 3 steps.

I have a theory for a structure that would allow fully automated destroy plans on archived repository resources, but it has a lot of caveats and potentially dangerous operations, so I don't think it is viable. Also, it wouldn't work on remove-and-apply, only on destroy.

If the GitHub API ever supports unarchiving, this becomes simpler, but still 3 steps (just unarchive, apply, then delete everything)

If you can convince the terraform developers to massively overhaul the graph generation system to support a resource being created and deleted after the resource it depends on, that would be a one step solution to this problem.

Finally, depending on your tolerance for time, you can create a potentially nondeterministic, but fully automated, solution to this problem that takes advantage of the principles in the immediately preceding line:

  1. Adjust any resources that depend on the repo to not depend on the repo (in this case, change collaborator's repository to be var.name)
  2. Create a null_resource resource
  3. Create a sufficiently long*** time_sleep resource
  4. Create depends_on blocks in the repository resource and the time_sleep resource that point to the null_resource resource
  5. Create a depends_on block in pointing to the time_sleep resource in any resource that should depend on the repository resource
  6. Create a sufficiently long*** time_sleep resource
  7. Create a depends_on block in the new time_sleep resource that points to all of the resources in line 5 (except the repository resource and the original time_sleep resource)
  8. Configure your Terraform CLI to run enough threads that the repository creation/destruction will always (at worst) run parallel with the time_sleep resource creation/destruction.

***: sufficiently long means:

  • In line 3: the time it takes to create the repository
  • In line 6: the time it takes to delete the repository

aidan-mundy avatar Oct 01 '21 04:10 aidan-mundy

Also, just a general code suggestion: This:

variable "allow_cicd-deployer_to_push" {
  // Whatever this is now
}

resource "github_repository_collaborator" "cicd-deployer_ro" {
  count      = var.allow_cicd-deployer_to_push == "true" ? 0 : 1
  repository = github_repository.repository.name
  username   = "cicd-deployer"
  permission = "pull"
}

resource "github_repository_collaborator" "cicd-deployer_rw" {
  count      = var.allow_cicd-deployer_to_push == "true" ? 1 : 0
  repository = github_repository.repository.name
  username   = "cicd-deployer"
  permission = "push"
}

should probably be this

variable "allow_cicd-deployer_to_push" {
  // Whatever this was before, but with this change:
  type = bool
}

resource "github_repository_collaborator" "cicd-deployer" {
  repository = github_repository.repository.name
  username   = "cicd-deployer"
  permission = var.allow_cicd-deployer_to_push ? "push" : "pull"
}

unless you have a veeeeeeeeery specific usecase where it won't work. Even then, the existence of that usecase probably indicates a larger problem that needs fixing.

aidan-mundy avatar Oct 01 '21 04:10 aidan-mundy

👋 Hey Friends, this issue has been automatically marked as stale because it has no recent activity. It will be closed if no further activity occurs. Please add the Status: Pinned label if you feel that this issue needs to remain open/active. Thank you for your contributions and help in keeping things tidy!

github-actions[bot] avatar Nov 30 '22 16:11 github-actions[bot]

The same problem happens with any other modification of the repo. I think archival should be executed last after any other changes.

E.g. I changed a description and added archive flag, and it failed with:

Repository was archived so is read-only.

moltar avatar Feb 06 '24 15:02 moltar