Skip to content

Commit 05a262e

Browse files
Merge pull request #277 from ahmet2mir/project-template-support
Adding support for project templates
2 parents 5b1a050 + 0e0a69e commit 05a262e

File tree

4 files changed

+216
-4
lines changed

4 files changed

+216
-4
lines changed

docs/resources/project.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,14 @@ The following arguments are supported:
8282

8383
* `push_rules` (Optional) Push rules for the project (documented below).
8484

85+
* `template_name` - (Optional) When used without use_custom_template, name of a built-in project template. When used with use_custom_template, name of a custom project template. This option is mutually exclusive with `template_project_id`.
86+
87+
* `template_project_id` - (Optional) When used with use_custom_template, project ID of a custom project template. This is preferable to using template_name since template_name may be ambiguous (enterprise edition). This option is mutually exclusive with `template_name`.
88+
89+
* `use_custom_template` - (Optional) Use either custom instance or group (with group_with_project_templates_id) project template (enterprise edition).
90+
91+
* `group_with_project_templates_id` - (Optional) For group-level custom templates, specifies ID of group from which all the custom project templates are sourced. Leave empty for instance-level templates. Requires use_custom_template to be true (enterprise edition).
92+
8593
## Attributes Reference
8694

8795
The following additional attributes are exported:

gitlab/resource_gitlab_project.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,26 @@ var resourceGitLabProjectSchema = map[string]*schema.Schema{
248248
},
249249
},
250250
},
251+
"template_name": {
252+
Type: schema.TypeString,
253+
Optional: true,
254+
ConflictsWith: []string{"template_project_id"},
255+
ForceNew: true,
256+
},
257+
"template_project_id": {
258+
Type: schema.TypeInt,
259+
Optional: true,
260+
ConflictsWith: []string{"template_name"},
261+
ForceNew: true,
262+
},
263+
"use_custom_template": {
264+
Type: schema.TypeBool,
265+
Optional: true,
266+
},
267+
"group_with_project_templates_id": {
268+
Type: schema.TypeInt,
269+
Optional: true,
270+
},
251271
}
252272

253273
func resourceGitlabProject() *schema.Resource {
@@ -342,6 +362,22 @@ func resourceGitlabProjectCreate(d *schema.ResourceData, meta interface{}) error
342362
options.ImportURL = gitlab.String(v.(string))
343363
}
344364

365+
if v, ok := d.GetOk("template_name"); ok {
366+
options.TemplateName = gitlab.String(v.(string))
367+
}
368+
369+
if v, ok := d.GetOk("template_project_id"); ok {
370+
options.TemplateProjectID = gitlab.Int(v.(int))
371+
}
372+
373+
if v, ok := d.GetOk("use_custom_template"); ok {
374+
options.UseCustomTemplate = gitlab.Bool(v.(bool))
375+
}
376+
377+
if v, ok := d.GetOk("group_with_project_templates_id"); ok {
378+
options.GroupWithProjectTemplatesID = gitlab.Int(v.(int))
379+
}
380+
345381
log.Printf("[DEBUG] create gitlab project %q", *options.Name)
346382

347383
project, _, err := client.Projects.CreateProject(options)

gitlab/resource_gitlab_project_test.go

Lines changed: 145 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,74 @@ max_file_size = 123
203203
Config: testAccGitlabProjectConfigPushRules(rInt, `author_email_regex = "foo_author"`),
204204
ExpectError: regexp.MustCompile(regexp.QuoteMeta("Project push rules are not supported in your version of GitLab")),
205205
},
206+
// Create a project using template name
207+
{
208+
Config: testAccGitlabProjectConfigTemplateName(rInt),
209+
Check: resource.ComposeTestCheckFunc(
210+
testAccCheckGitlabProjectExists("gitlab_project.template-name", &received),
211+
testAccCheckGitlabProjectDefaultBranch(&received, &testAccGitlabProjectExpectedAttributes{
212+
DefaultBranch: "master",
213+
}),
214+
func(state *terraform.State) error {
215+
client := testAccProvider.Meta().(*gitlab.Client)
216+
217+
projectID := state.RootModule().Resources["gitlab_project.template-name"].Primary.ID
218+
219+
_, _, err := client.RepositoryFiles.GetFile(projectID, ".ruby-version", &gitlab.GetFileOptions{Ref: gitlab.String("master")}, nil)
220+
if err != nil {
221+
return fmt.Errorf("failed to get '.ruby-version' file from template project: %w", err)
222+
}
223+
224+
return nil
225+
},
226+
),
227+
},
228+
// Create a project using custom template name
229+
{
230+
Config: testAccGitlabProjectConfigTemplateNameCustom(rInt),
231+
SkipFunc: isRunningInCE,
232+
Check: resource.ComposeTestCheckFunc(
233+
testAccCheckGitlabProjectExists("gitlab_project.template-name-custom", &received),
234+
testAccCheckGitlabProjectDefaultBranch(&received, &testAccGitlabProjectExpectedAttributes{
235+
DefaultBranch: "master",
236+
}),
237+
func(state *terraform.State) error {
238+
client := testAccProvider.Meta().(*gitlab.Client)
239+
240+
projectID := state.RootModule().Resources["gitlab_project.template-name-custom"].Primary.ID
241+
242+
_, _, err := client.RepositoryFiles.GetFile(projectID, "Gemfile", &gitlab.GetFileOptions{Ref: gitlab.String("master")}, nil)
243+
if err != nil {
244+
return fmt.Errorf("failed to get 'Gemfile' file from template project: %w", err)
245+
}
246+
247+
return nil
248+
},
249+
),
250+
},
251+
// Create a project using custom template project id
252+
{
253+
Config: testAccGitlabProjectConfigTemplateProjectID(rInt),
254+
SkipFunc: isRunningInCE,
255+
Check: resource.ComposeTestCheckFunc(
256+
testAccCheckGitlabProjectExists("gitlab_project.template-id", &received),
257+
testAccCheckGitlabProjectDefaultBranch(&received, &testAccGitlabProjectExpectedAttributes{
258+
DefaultBranch: "master",
259+
}),
260+
func(state *terraform.State) error {
261+
client := testAccProvider.Meta().(*gitlab.Client)
262+
263+
projectID := state.RootModule().Resources["gitlab_project.template-id"].Primary.ID
264+
265+
_, _, err := client.RepositoryFiles.GetFile(projectID, "Rakefile", &gitlab.GetFileOptions{Ref: gitlab.String("master")}, nil)
266+
if err != nil {
267+
return fmt.Errorf("failed to get 'Rakefile' file from template project: %w", err)
268+
}
269+
270+
return nil
271+
},
272+
),
273+
},
206274
// Update to original project config
207275
{
208276
Config: testAccGitlabProjectConfig(rInt),
@@ -224,7 +292,7 @@ func TestAccGitlabProject_initializeWithReadme(t *testing.T) {
224292
Config: testAccGitlabProjectConfigInitializeWithReadme(rInt),
225293
Check: resource.ComposeTestCheckFunc(
226294
testAccCheckGitlabProjectExists("gitlab_project.foo", &project),
227-
testAccCheckGitlabProjectInitializeWithReadme(&project, &testAccGitlabProjectExpectedAttributes{
295+
testAccCheckGitlabProjectDefaultBranch(&project, &testAccGitlabProjectExpectedAttributes{
228296
DefaultBranch: "master",
229297
}),
230298
),
@@ -437,6 +505,22 @@ func TestAccGitlabProject_importURL(t *testing.T) {
437505
})
438506
}
439507

508+
func TestAccGitlabProjec_templateMutualExclusiveNameAndID(t *testing.T) {
509+
rInt := acctest.RandInt()
510+
511+
resource.Test(t, resource.TestCase{
512+
PreCheck: func() { testAccPreCheck(t) },
513+
Providers: testAccProviders,
514+
Steps: []resource.TestStep{
515+
{
516+
Config: testAccCheckMutualExclusiveNameAndID(rInt),
517+
SkipFunc: isRunningInCE,
518+
ExpectError: regexp.MustCompile(regexp.QuoteMeta(`"template_project_id": conflicts with template_name`)),
519+
},
520+
},
521+
})
522+
}
523+
440524
func testAccCheckGitlabProjectExists(n string, project *gitlab.Project) resource.TestCheckFunc {
441525
return func(s *terraform.State) error {
442526
var err error
@@ -506,10 +590,10 @@ func testAccCheckAggregateGitlabProject(expected, received *gitlab.Project) reso
506590
return resource.ComposeAggregateTestCheckFunc(checks...)
507591
}
508592

509-
func testAccCheckGitlabProjectInitializeWithReadme(project *gitlab.Project, want *testAccGitlabProjectExpectedAttributes) resource.TestCheckFunc {
593+
func testAccCheckGitlabProjectDefaultBranch(project *gitlab.Project, want *testAccGitlabProjectExpectedAttributes) resource.TestCheckFunc {
510594
return func(s *terraform.State) error {
511595
if project.DefaultBranch != want.DefaultBranch {
512-
return fmt.Errorf("got description %q; want %q", project.DefaultBranch, want.DefaultBranch)
596+
return fmt.Errorf("got default branch %q; want %q", project.DefaultBranch, want.DefaultBranch)
513597
}
514598

515599
return nil
@@ -765,3 +849,61 @@ resource "gitlab_project" "foo" {
765849
}
766850
`, rInt, pushRules)
767851
}
852+
853+
func testAccGitlabProjectConfigTemplateName(rInt int) string {
854+
return fmt.Sprintf(`
855+
resource "gitlab_project" "template-name" {
856+
name = "template-name-%d"
857+
path = "template-name.%d"
858+
description = "Terraform acceptance tests"
859+
template_name = "rails"
860+
default_branch = "master"
861+
}
862+
`, rInt, rInt)
863+
}
864+
865+
// 2020-09-07: Currently Gitlab (version 13.3.6 ) doesn't allow in admin API
866+
// ability to set a group as instance level templates.
867+
// To test resource_gitlab_project_test template features we add
868+
// group, project myrails and admin settings directly in scripts/start-gitlab.sh
869+
// Once Gitlab add admin template in API we could manage group/project/settings
870+
// directly in tests like TestAccGitlabProject_basic.
871+
func testAccGitlabProjectConfigTemplateNameCustom(rInt int) string {
872+
return fmt.Sprintf(`
873+
resource "gitlab_project" "template-name-custom" {
874+
name = "template-name-custom-%d"
875+
path = "template-name-custom.%d"
876+
description = "Terraform acceptance tests"
877+
template_name = "myrails"
878+
use_custom_template = true
879+
default_branch = "master"
880+
}
881+
`, rInt, rInt)
882+
}
883+
884+
func testAccGitlabProjectConfigTemplateProjectID(rInt int) string {
885+
return fmt.Sprintf(`
886+
resource "gitlab_project" "template-id" {
887+
name = "template-id-%d"
888+
path = "template-id.%d"
889+
description = "Terraform acceptance tests"
890+
template_project_id = 999
891+
use_custom_template = true
892+
default_branch = "master"
893+
}
894+
`, rInt, rInt)
895+
}
896+
897+
func testAccCheckMutualExclusiveNameAndID(rInt int) string {
898+
return fmt.Sprintf(`
899+
resource "gitlab_project" "template-mutual-exclusive" {
900+
name = "template-mutual-exclusive-%d"
901+
path = "template-mutual-exclusive.%d"
902+
description = "Terraform acceptance tests"
903+
template_name = "rails"
904+
template_project_id = 999
905+
use_custom_template = true
906+
default_branch = "master"
907+
}
908+
`, rInt, rInt)
909+
}

scripts/start-gitlab.sh

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,4 +41,30 @@ echo "Creating access token"
4141
) |
4242
docker exec -i gitlab gitlab-rails console
4343

44-
44+
# 2020-09-07: Currently Gitlab (version 13.3.6 ) doesn't allow in admin API
45+
# ability to set a group as instance level templates.
46+
# To test resource_gitlab_project_test template features we add
47+
# group, project myrails and admin settings directly in scripts/start-gitlab.sh
48+
# Once Gitlab add admin template in API we could manage group/project/settings
49+
# directly in tests like TestAccGitlabProject_basic.
50+
# Works on CE too
51+
echo
52+
echo "Creating an instance level template group with a simple template based on rails"
53+
(
54+
echo -n 'group_template = Group.new('
55+
echo -n 'name: :terraform, '
56+
echo -n 'path: :terraform);'
57+
echo -n 'group_template.save!;'
58+
echo -n 'application_settings = ApplicationSetting.find_by "";'
59+
echo -n 'application_settings.custom_project_templates_group_id = group_template.id;'
60+
echo -n 'application_settings.save!;'
61+
echo -n 'attrs = {'
62+
echo -n 'name: :myrails, '
63+
echo -n 'path: :myrails, '
64+
echo -n 'namespace_id: group_template.id, '
65+
echo -n 'template_name: :rails, '
66+
echo -n 'id: 999};'
67+
echo -n 'project = ::Projects::CreateService.new(User.find_by_username("root"), attrs).execute;'
68+
echo -n 'project.saved?;'
69+
) |
70+
docker exec -i gitlab gitlab-rails console

0 commit comments

Comments
 (0)