diff --git a/client/project.go b/client/project.go index 1bec4150..ea5f4530 100644 --- a/client/project.go +++ b/client/project.go @@ -55,6 +55,67 @@ func ProjectBody(d *schema.ResourceData) models.ProjectsBodyPost { return body } +// GetScannerByName lists all scanners and returns the one matching the given name. +func (client *Client) GetScannerByName(scanner string) (models.ScannerBody, error) { + resp, _, _, err := client.SendRequest("GET", models.PathScanners, nil, 0) + if err != nil { + return models.ScannerBody{}, err + } + + var scanners []models.ScannerBody + err = json.Unmarshal([]byte(resp), &scanners) + if err != nil { + return models.ScannerBody{}, err + } + + for _, v := range scanners { + if strings.EqualFold(v.Name, scanner) { + return v, nil + } + } + + return models.ScannerBody{}, fmt.Errorf("scanner %q not found", scanner) +} + +// SetProjectScanner sets the vulnerability scanner for a specific project. +func (client *Client) SetProjectScanner(d *schema.ResourceData) error { + scanner := d.Get("vulnerability_scanner").(string) + if scanner == "" { + return nil + } + + scannerData, err := client.GetScannerByName(scanner) + if err != nil { + return err + } + + body := models.ProjectScannerBody{ + UUID: scannerData.UUID, + } + + _, _, _, err = client.SendRequest("PUT", d.Id()+"/scanner", body, 200) + return err +} + +// GetProjectScanner returns the name of the scanner assigned to a project. +func (client *Client) GetProjectScanner(projectPath string) (string, error) { + resp, _, respCode, err := client.SendRequest("GET", projectPath+"/scanner", nil, 200) + if err != nil { + if respCode == 404 { + return "", nil + } + return "", err + } + + var scannerData models.ScannerBody + err = json.Unmarshal([]byte(resp), &scannerData) + if err != nil { + return "", err + } + + return scannerData.Name, nil +} + func expandCveAllowList(cveAllowlist []interface{}) models.CveAllowlistItems { allowlist := models.CveAllowlistItems{} diff --git a/client/system.go b/client/system.go index 6cae8165..0c95d573 100644 --- a/client/system.go +++ b/client/system.go @@ -1,9 +1,7 @@ package client import ( - "encoding/json" "log" - "strings" "github.com/goharbor/terraform-provider-harbor/models" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" @@ -77,27 +75,15 @@ func (client *Client) SetSchedule(d *schema.ResourceData, scheduleType string) ( // SetDefaultScanner set the default scanner within harbor func (client *Client) SetDefaultScanner(scanner string) (err error) { - resp, _, _, err := client.SendRequest("GET", models.PathScanners, nil, 0) - - body := models.ScannerBody{ - IsDefault: true, - } - - var jsonData []models.ScannerBody - err = json.Unmarshal([]byte(resp), &jsonData) + scannerData, err := client.GetScannerByName(scanner) if err != nil { return err } - for _, v := range jsonData { - - if v.Name == strings.Title(scanner) { - _, _, _, err = client.SendRequest("PATCH", models.PathScanners+"/"+v.UUID, body, 0) - } - if err != nil { - return err - } + body := models.ScannerBody{ + IsDefault: true, } - return nil + _, _, _, err = client.SendRequest("PATCH", models.PathScanners+"/"+scannerData.UUID, body, 0) + return err } diff --git a/docs/data-sources/project.md b/docs/data-sources/project.md index 57eb9ea0..b2216f38 100644 --- a/docs/data-sources/project.md +++ b/docs/data-sources/project.md @@ -35,3 +35,4 @@ output "project_id" { - `public` (Boolean) If the project has public accessibility. - `type` (String) The type of the project : Project or ProxyCache. - `vulnerability_scanning` (Boolean) If the images is scanned for vulnerabilities when push to harbor. +- `vulnerability_scanner` (String) The name of the vulnerability scanner assigned to the project. diff --git a/docs/resources/project.md b/docs/resources/project.md index 5319f138..17223e39 100644 --- a/docs/resources/project.md +++ b/docs/resources/project.md @@ -19,6 +19,7 @@ resource "harbor_project" "main" { name = "main" public = false # (Optional) Default value is false vulnerability_scanning = true # (Optional) Default value is true. Automatically scan images on push + vulnerability_scanner = "Trivy" # (Optional) Override the global default scanner for this project enable_content_trust = true # (Optional) Default value is false. Deny unsigned images from being pulled (notary) enable_content_trust_cosign = false # (Optional) Default value is false. Deny unsigned images from being pulled (cosign) auto_sbom_generation = true # (Optional) Default value is false. Automatically generate SBOMs for images @@ -56,6 +57,7 @@ resource "harbor_registry" "docker" { - `public` (Boolean) The project will be public accessibility.(Default: `false`) - `storage_quota` (Number) The storage quota of the project in GB's. - `vulnerability_scanning` (Boolean) Images will be scanned for vulnerabilities when push to harbor. (Default: `true`) +- `vulnerability_scanner` (String) The name of the vulnerability scanner to use for this project, overriding the global default scanner. If not set, the project uses the global default scanner configured via `harbor_interrogation_services`. - `auto_sbom_generation` (Boolean) Automatically generate SBOM for images pushed to this project. (Default: `false`) can only be used with Harbor version v2.11.0 and above ### Specific for Proxy Project diff --git a/models/projects.go b/models/projects.go index 42f6858a..6cc67710 100644 --- a/models/projects.go +++ b/models/projects.go @@ -65,6 +65,10 @@ type ProjectsBodyResponses struct { } `json:"metadata"` } +type ProjectScannerBody struct { + UUID string `json:"uuid"` +} + type CveAllowlistItems []struct { CveID string `json:"cve_id,omitempty"` } diff --git a/provider/data_project.go b/provider/data_project.go index 5403c5aa..5c097f5f 100644 --- a/provider/data_project.go +++ b/provider/data_project.go @@ -34,6 +34,10 @@ func dataProject() *schema.Resource { Type: schema.TypeBool, Computed: true, }, + "vulnerability_scanner": { + Type: schema.TypeString, + Computed: true, + }, }, } } @@ -68,6 +72,12 @@ func dataProjectRead(d *schema.ResourceData, m interface{}) error { d.Set("public", public) d.Set("vulnerability_scanning", autoScan) d.Set("type", project_type) + + scannerName, err := apiClient.GetProjectScanner(id) + if err != nil { + return err + } + d.Set("vulnerability_scanner", scannerName) } } return nil diff --git a/provider/resource_project.go b/provider/resource_project.go index dc5fdea9..5c03ac16 100644 --- a/provider/resource_project.go +++ b/provider/resource_project.go @@ -39,6 +39,10 @@ func resourceProject() *schema.Resource { Optional: true, Default: true, }, + "vulnerability_scanner": { + Type: schema.TypeString, + Optional: true, + }, "storage_quota": { Type: schema.TypeInt, Optional: true, @@ -136,6 +140,11 @@ func resourceProjectCreate(d *schema.ResourceData, m interface{}) error { } } + err = apiClient.SetProjectScanner(d) + if err != nil { + return err + } + return resourceProjectRead(d, m) } @@ -223,6 +232,14 @@ func resourceProjectRead(d *schema.ResourceData, m interface{}) error { } d.Set("cve_allowlist", cveAllowlist) + if d.Get("vulnerability_scanner").(string) != "" { + scannerName, err := apiClient.GetProjectScanner(d.Id()) + if err != nil { + return err + } + d.Set("vulnerability_scanner", scannerName) + } + return nil } @@ -241,6 +258,13 @@ func resourceProjectUpdate(d *schema.ResourceData, m interface{}) error { apiClient.UpdateStorageQuota(d) + if d.HasChange("vulnerability_scanner") { + err = apiClient.SetProjectScanner(d) + if err != nil { + return err + } + } + return resourceProjectRead(d, m) }