Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add jenkins_job_config data resource #4

Merged
merged 2 commits into from
Aug 11, 2022
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
30 changes: 30 additions & 0 deletions docs/data-sources/job_config.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# jenkins_job_config Data Source

Get the XML configuration of a job within Jenkins.

## Example Usage

```hcl
data "jenkins_job_config" "example" {
name = "job-name"
xml_node = "xml-node-name"
}
```

## Argument Reference

The following arguments are supported:

* `name` - (Required) The name of the job being read.
* `folder` - (Optional) The folder namespace containing this job.
* `xml_node` - (Optional) The xml_node used to filter the XML configuration of the job.
* `regex` - (Optional) The regex used to filter the XML configuration of the job.

> Note: If both `xml_node` and `regex` are empty, the complete XML configuration of the job will be exported.

## Attribute Reference

In addition to all arguments above, the following attributes are exported:

* `id` - The full canonical job path, E.G. `/job/job-name`.
* `config` - The XML configuration of the job.
102 changes: 102 additions & 0 deletions jenkins/data_source_jenkins_job_config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package jenkins

import (
"context"
"errors"
"fmt"
"log"
"regexp"

"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)

func dataSourceJenkinsJobConfig() *schema.Resource {
return &schema.Resource{
ReadContext: dataSourceJenkinsJobConfigRead,
Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Description: "The unique name of the JenkinsCI job.",
Required: true,
ValidateDiagFunc: validateJobName,
},
"folder": {
Type: schema.TypeString,
Description: "The folder namespace that the job exists in.",
Optional: true,
ValidateDiagFunc: validateFolderName,
},
"xml_node": {
Type: schema.TypeString,
Optional: true,
Description: "The xml_node used to filter the job configuration.",
},
"regex": {
Type: schema.TypeString,
Optional: true,
Description: "The regex used to filter the job configuration.",
},
"config": {
Type: schema.TypeString,
Computed: true,
Description: "The XML configuration of the job.",
},
},
}
}

func dataSourceJenkinsJobConfigRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
client := meta.(*jenkinsAdapter)

name := d.Get("name").(string)
folderName := d.Get("folder").(string)

id := formatFolderName(folderName + "/" + name)
job, err := client.GetJob(ctx, id)
if err != nil {
log.Printf("[DEBUG] jenkins::job::config - Could not find job %q: %s", id, err.Error())
return nil
}
config, err := job.GetConfig(ctx)
if err != nil {
return diag.FromErr(fmt.Errorf("jenkins::job::config - Error get config in job %q: %w", id, err))
}

xmlNode := d.Get("xml_node").(string)
if xmlNode != "" {
config, err = filterJobConfigByXMLNode(config, xmlNode)
if err != nil {
log.Printf("[DEBUG] jenkins::job::config - Job %q: %s", id, err.Error())
}
}

regex := d.Get("regex").(string)
if regex != "" && config != "" {
config, err = filterJobConfigByRegex(config, regex)
if err != nil {
log.Printf("[DEBUG] jenkins::job::config - Job %q: %s", id, err.Error())
}
}

d.Set("config", config)
d.SetId(job.Base)
return nil
}

func filterJobConfigByRegex(config string, regex string) (string, error) {
re := regexp.MustCompile(fmt.Sprintf("%s", regex))
result := re.FindStringSubmatch(config)
if len(result) == 0 {
return "", errors.New("no match result")
}
return result[0], nil
}

func filterJobConfigByXMLNode(config string, node string) (string, error) {
result, err := filterJobConfigByRegex(config, fmt.Sprintf(`<%s>[^\0]*</%s>|<%s/>`, node, node, node))
if err != nil {
return "", fmt.Errorf("not found XML node: %q", node)
}
return result, nil
}
84 changes: 84 additions & 0 deletions jenkins/data_source_jenkins_job_config_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package jenkins

import (
"fmt"
"os"
"path/filepath"
"testing"

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

func TestAccJenkinsJobConfigDataSource_basic(t *testing.T) {
testDir := t.TempDir()
_ = os.WriteFile(filepath.Join(testDir, "test.xml"), testXML, 0644)
randString := acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum)

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
Steps: []resource.TestStep{
{
Config: fmt.Sprintf(`
resource jenkins_job foo {
name = "tf-acc-test-%s"
template = templatefile("%s/test.xml", {
description = "Acceptance testing Jenkins provider"
})
}

data jenkins_job_config foo {
name = jenkins_job.foo.name
xml_node = "properties"
}`, randString, testDir),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("jenkins_job.foo", "id", "/job/tf-acc-test-"+randString),
resource.TestCheckResourceAttr("data.jenkins_job_config.foo", "id", "/job/tf-acc-test-"+randString),
resource.TestCheckResourceAttr("data.jenkins_job_config.foo", "name", "tf-acc-test-"+randString),
resource.TestCheckResourceAttr("data.jenkins_job_config.foo", "config", "<properties/>"),
),
},
},
})
}

func TestAccJenkinsJobConfigDataSource_nested(t *testing.T) {
testDir := t.TempDir()
_ = os.WriteFile(filepath.Join(testDir, "test.xml"), testXML, 0644)
randString := acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum)

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
Steps: []resource.TestStep{
{
Config: fmt.Sprintf(`
resource jenkins_folder foo {
name = "tf-acc-test-%s"
}

resource jenkins_job sub {
name = "subfolder"
folder = jenkins_folder.foo.id
template = templatefile("%s/test.xml", {
description = "Acceptance testing Jenkins provider"
})
}

data jenkins_job_config sub {
name = jenkins_job.sub.name
folder = jenkins_job.sub.folder
regex = "<properties>[^\\0]*</properties>|<properties/>"
}`, randString, testDir),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("jenkins_folder.foo", "id", "/job/tf-acc-test-"+randString),
resource.TestCheckResourceAttr("jenkins_job.sub", "id", "/job/tf-acc-test-"+randString+"/job/subfolder"),
resource.TestCheckResourceAttr("data.jenkins_job_config.sub", "name", "subfolder"),
resource.TestCheckResourceAttr("data.jenkins_job_config.sub", "folder", "/job/tf-acc-test-"+randString),
resource.TestCheckResourceAttr("data.jenkins_job_config.sub", "config", "<properties/>"),
),
},
},
})
}
1 change: 1 addition & 0 deletions jenkins/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ func Provider() *schema.Provider {
"jenkins_credential_vault_approle": dataSourceJenkinsCredentialVaultAppRole(),
"jenkins_folder": dataSourceJenkinsFolder(),
"jenkins_job": dataSourceJenkinsJob(),
"jenkins_job_config": dataSourceJenkinsJobConfig(),
"jenkins_plugins": dataSourceJenkinsPlugins(),
},

Expand Down