terraform-provider-gitlab
terraform-provider-gitlab copied to clipboard
Attempts to change path of project if container registry tags exist succeed in Terraform, but this action is not allowed by Gitlab
GitLab Provider version
3.16.1
GitLab version
15.3.0-pre
Terraform version
1.2.5
Relevant Terraform Configuration
module "project_obs_ci_images" {
source = "app.terraform.io/oklahoma-biological-survey/project/gitlab"
version = "~>2.0.0"
name = "OBS CI Images"
path = "obs-ci-images"
description = "Oklahoma Biological Survey CI Images"
group_id = module.group_oklahoma_biological_survey.group_id
}
Relevant log output
Terraform will perform the following actions:
# module.project_obs_ci_images.gitlab_project.this will be updated in-place
~ resource "gitlab_project" "this" {
~ description = "Oklahoma Biological Survey CI Build Images" -> "Oklahoma Biological Survey CI Images"
~ http_url_to_repo = "https://gitlab.com/oklahoma-biological-survey/build-images.git" -> (known after apply)
id = "<project_id>"
~ name = "Build Images" -> "OBS CI Images"
~ path = "build-images" -> "obs-ci-images"
~ path_with_namespace = "oklahoma-biological-survey/build-images" -> (known after apply)
~ ssh_url_to_repo = "[email protected]:oklahoma-biological-survey/build-images.git" -> (known after apply)
tags = []
~ web_url = "https://gitlab.com/oklahoma-biological-survey/build-images" -> (known after apply)
# (55 unchanged attributes hidden)
# (1 unchanged block hidden)
}
Plan: 0 to add, 1 to change, 0 to destroy.
Description
I've changed the name of an existing project that is managed with Terraform and has existing container registry tags. The original configuration is listed below (updated configuration is in the Relevant Terraform Configuration
section):
module "project_build_images" {
source = "app.terraform.io/oklahoma-biological-survey/project/gitlab"
version = "~>2.0.0"
name = "Build Images"
path = "build-images"
description = "Oklahoma Biological Survey CI Build Images"
group_id = module.group_oklahoma_biological_survey.group_id
}
I moved these resources in the state (considering I renamed the module) prior to running Terraform. The plan continues to show the output above, even after running the apply multiple times. The apply is always successful (as shown below), but no changes are made to the state file and these changes are not reflected in Gitlab (SaaS offering).
After trying to make these changes manually in the UI (luckily I have Owner level access to my Gitlab instance), I receive this error message when updating the project's path:
Cannot rename project because it contains container registry tags!
This seems like a limitation of the Gitlab API, but I'm thinking that the Terraform provider should also show this error message, as it seems like the changes were made, but they certainly were not.
Output after apply is ran:
module.project_obs_ci_images.gitlab_project.this: Modifying... [id=<project_id>]
module.project_obs_ci_images.gitlab_project.this: Modifications complete after 1s [id=<project_id>]
Apply complete! Resources: 0 added, 1 changed, 0 destroyed.
Yes, it should actually yield the same errors than the API!
Do you mind providing a standalone example using the gitlab_project
resource directly?
That was my thinking as well!
Here is the resource configuration within my module. The example I provided in the initial report was modifying the value for var.path
(among others). Is this sufficient? Happy to provide more information if need be.
resource "gitlab_project" "this" {
name = var.name
path = var.path
description = var.description
namespace_id = var.group_id
default_branch = var.default_branch
visibility_level = "private"
merge_requests_enabled = true
remove_source_branch_after_merge = true
initialize_with_readme = false
request_access_enabled = false
shared_runners_enabled = false
archived = var.archived
approvals_before_merge = var.num_approvals
tags = null
only_allow_merge_if_pipeline_succeeds = var.merge_request_pipelines_enabled
allow_merge_on_skipped_pipeline = var.merge_request_pipelines_enabled
only_allow_merge_if_all_discussions_are_resolved = true
build_git_strategy = "clone"
}
FYI, I created an indicative test for this (note - to run this test you need to make some changes to the docker-compose file and you need to add a hostfile entry on your localhost as well):
func TestAccGitlabProject_withContainer(t *testing.T) {
var received gitlab.Project
rInt := acctest.RandInt()
resource.ParallelTest(t, resource.TestCase{
ProviderFactories: providerFactories,
CheckDestroy: testAccCheckGitlabProjectDestroy,
Steps: []resource.TestStep{
{
Config: fmt.Sprintf(`
resource "gitlab_project" "this" {
name = "foo-%d"
visibility_level = "public"
}`, rInt),
Check: resource.ComposeTestCheckFunc(
testAccCheckGitlabProjectExists("gitlab_project.this", &received),
func(s *terraform.State) error {
exec.Command("docker login -u root --password ACCTEST1234567890123 localhost:5050").Run()
exec.Command("docker pull alpine").Run()
path := fmt.Sprintf("localhost:5050/%s", received.PathWithNamespace)
exec.Command(fmt.Sprintf("docker tag alpine %s", path)).Run()
exec.Command(fmt.Sprintf("docker push %s", path)).Run()
return nil
},
),
},
// Change Name
{
Config: fmt.Sprintf(`
resource "gitlab_project" "this" {
name = "bar-%d"
visibility_level = "public"
}`, rInt),
Check: resource.ComposeTestCheckFunc(
func(s *terraform.State) error {
// here for debugging breakpoint purposes.
log.Println("stuff")
return nil
},
),
},
// Verify Import
{
ResourceName: "gitlab_project.this",
ImportState: true,
ImportStateVerify: true,
},
},
})
}
I can confirm the test passes, and notably, it passes because it successfully renames the project while the container registry tag is present. Maybe this is failing because in addition to changing the project name, you're changing the namespace as well?
Before rename:
After rename:
@PatrickRice-KSC can you change the path
instead? AFAIK, the name is merely used to display a name.
It may not even require a namespace change (as in the namespace_id
.
can you change the
path
instead? AFAIK, the name is merely used to display a name. It may not even require a namespace change (as in thenamespace_id
.
For sure - I'll give it a whirl here shortly.
Yes, I was able to successfully rename the project from the UI with no issues, but the error occurred when I changed the path.
Here we go, updated test:
func TestAccGitlabProject_withContainer(t *testing.T) {
var received gitlab.Project
rInt := acctest.RandInt()
resource.ParallelTest(t, resource.TestCase{
ProviderFactories: providerFactories,
CheckDestroy: testAccCheckGitlabProjectDestroy,
Steps: []resource.TestStep{
{
Config: fmt.Sprintf(`
resource "gitlab_project" "this" {
name = "foo-%d"
path = "test"
visibility_level = "public"
}`, rInt),
Check: resource.ComposeTestCheckFunc(
testAccCheckGitlabProjectExists("gitlab_project.this", &received),
func(s *terraform.State) error {
exec.Command("/usr/bin/docker login -u root --password ACCTEST1234567890123 localhost:5050").Run()
exec.Command("/usr/bin/docker pull alpine").Run()
path := fmt.Sprintf("localhost:5050/%s", received.PathWithNamespace)
exec.Command(fmt.Sprintf("/usr/bin/docker tag alpine %s", path)).Run()
exec.Command(fmt.Sprintf("/usr/bin/docker push %s", path)).Run()
return nil
},
),
},
// Change Name
{
Config: fmt.Sprintf(`
resource "gitlab_project" "this" {
name = "foo-%d"
path = "test-change"
visibility_level = "public"
}`, rInt),
Check: resource.ComposeTestCheckFunc(
func(s *terraform.State) error {
log.Println("stuff")
return nil
},
),
},
},
})
}
Result: test fails properly, with the path not changing in GitLab:
However, it's worth noting that based on the way the test fails, you can see it's not that the apply failed (which I think is what we'd expect here) - it's failing because the plan was not empty. I think that mirrors the behavior that @twalker1998 is talking that, where the apply should fail, and it doesn't.
--- FAIL: TestAccGitlabProject_withContainer (47.21s)
resource_gitlab_project_test.go:30: Step 2/2 error: After applying this test step, the plan was not empty.
stdout:
An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
~ update in-place
Terraform will perform the following actions:
# gitlab_project.this will be updated in-place
~ resource "gitlab_project" "this" {
allow_merge_on_skipped_pipeline = false
analytics_access_level = "enabled"
approvals_before_merge = 0
archived = false
auto_cancel_pending_pipelines = "enabled"
auto_devops_deploy_strategy = "continuous"
auto_devops_enabled = true
autoclose_referenced_issues = true
build_git_strategy = "fetch"
build_timeout = 3600
builds_access_level = "enabled"
ci_default_git_depth = 20
ci_forward_deployment_enabled = true
container_registry_access_level = "enabled"
container_registry_enabled = true
default_branch = "main"
emails_disabled = false
forking_access_level = "enabled"
~ http_url_to_repo = "http://gitlab/root/test.git" -> (known after apply)
id = "13"
issues_access_level = "enabled"
issues_enabled = true
lfs_enabled = true
merge_method = "merge"
merge_pipelines_enabled = false
merge_requests_access_level = "enabled"
merge_requests_enabled = true
merge_trains_enabled = false
mirror = false
mirror_overwrites_diverged_branches = false
mirror_trigger_builds = false
name = "foo-1817053023237789349"
namespace_id = 1
only_allow_merge_if_all_discussions_are_resolved = false
only_allow_merge_if_pipeline_succeeds = false
only_mirror_protected_branches = false
operations_access_level = "enabled"
packages_enabled = true
pages_access_level = "private"
~ path = "test" -> "test-change"
~ path_with_namespace = "root/test" -> (known after apply)
pipelines_enabled = true
printing_merge_request_link_enabled = true
public_builds = false
remove_source_branch_after_merge = false
repository_access_level = "enabled"
repository_storage = "default"
request_access_enabled = true
resolve_outdated_diff_discussions = false
runners_token = (sensitive value)
security_and_compliance_access_level = "private"
shared_runners_enabled = false
snippets_access_level = "enabled"
snippets_enabled = true
squash_option = "default_off"
~ ssh_url_to_repo = "git@gitlab:root/test.git" -> (known after apply)
tags = []
topics = []
visibility_level = "public"
~ web_url = "http://gitlab/root/test" -> (known after apply)
wiki_access_level = "enabled"
wiki_enabled = true
container_expiration_policy {
cadence = "1d"
enabled = false
keep_n = 10
next_run_at = "2022-08-01T02:58:07Z"
older_than = "90d"
}
}
Plan: 0 to add, 1 to change, 0 to destroy.
FAIL
@twalker1998
This seems like a limitation of the Gitlab API, ...
Yes, it indeed seems like a limitation of the GitLab API. Running a curl
command manually against a project with existing images in the container registry responds with a 200
, but a null
body.
..., but I'm thinking that the Terraform provider should also show this error message, as it seems like the changes were made, but they certainly were not.
Yes, it should and actually it would already if the GitLab API would return a non-success status code and would display the response body in the error message.
@twalker1998 do you mind creating the issue upstream?
@timofurrer - WDYT about adding a StateChangeConf
to the path of the project, with a very short timeout (5-10s)? That way the apply would error with a "timeout waiting for project to reach state" error on apply in the tf provider, and would transparently be fixed in the provider when the upstream change was made?