mirror of
https://github.com/goharbor/harbor.git
synced 2025-02-22 23:01:33 +01:00
Merge pull request #2543 from reasonerjt/clair-integration
provide POST api/repostitores/xxx/tags/xxx/scan to trigger image scan
This commit is contained in:
commit
9fb0ad6c0d
@ -80,3 +80,9 @@ type ComponentsOverviewEntry struct {
|
|||||||
Sev int `json:"severity"`
|
Sev int `json:"severity"`
|
||||||
Count int `json:"count"`
|
Count int `json:"count"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ImageScanReq represents the request body to send to job service for image scan
|
||||||
|
type ImageScanReq struct {
|
||||||
|
Repo string `json:"repository"`
|
||||||
|
Tag string `json:"tag"`
|
||||||
|
}
|
||||||
|
@ -31,20 +31,15 @@ type ImageScanJob struct {
|
|||||||
jobBaseAPI
|
jobBaseAPI
|
||||||
}
|
}
|
||||||
|
|
||||||
type imageScanReq struct {
|
|
||||||
Repo string `json:"repository"`
|
|
||||||
Tag string `json:"tag"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prepare ...
|
// Prepare ...
|
||||||
func (isj *ImageScanJob) Prepare() {
|
func (isj *ImageScanJob) Prepare() {
|
||||||
//TODO:add authenticate to check secret when integrate with UI API.
|
//TODO Uncomment to enable security check.
|
||||||
//isj.authenticate()
|
// isj.authenticate()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Post creates a scanner job and hand it to statemachine.
|
// Post creates a scanner job and hand it to statemachine.
|
||||||
func (isj *ImageScanJob) Post() {
|
func (isj *ImageScanJob) Post() {
|
||||||
var data imageScanReq
|
var data models.ImageScanReq
|
||||||
isj.DecodeJSONReq(&data)
|
isj.DecodeJSONReq(&data)
|
||||||
log.Debugf("data: %+v", data)
|
log.Debugf("data: %+v", data)
|
||||||
regURL, err := config.LocalRegURL()
|
regURL, err := config.LocalRegURL()
|
||||||
|
@ -610,7 +610,6 @@ func (ra *RepositoryAPI) GetTopRepos() {
|
|||||||
//GetSignatures returns signatures of a repository
|
//GetSignatures returns signatures of a repository
|
||||||
func (ra *RepositoryAPI) GetSignatures() {
|
func (ra *RepositoryAPI) GetSignatures() {
|
||||||
repoName := ra.GetString(":splat")
|
repoName := ra.GetString(":splat")
|
||||||
|
|
||||||
targets, err := notary.GetInternalTargets(config.InternalNotaryEndpoint(),
|
targets, err := notary.GetInternalTargets(config.InternalNotaryEndpoint(),
|
||||||
ra.SecurityCtx.GetUsername(), repoName)
|
ra.SecurityCtx.GetUsername(), repoName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -621,6 +620,43 @@ func (ra *RepositoryAPI) GetSignatures() {
|
|||||||
ra.ServeJSON()
|
ra.ServeJSON()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//ScanImage handles request POST /api/repository/$repository/tags/$tag/scan to trigger image scan manually.
|
||||||
|
func (ra *RepositoryAPI) ScanImage() {
|
||||||
|
if !config.WithClair() {
|
||||||
|
log.Warningf("Harbor is not deployed with Clair, scan is disabled.")
|
||||||
|
ra.RenderError(http.StatusServiceUnavailable, "")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
repoName := ra.GetString(":splat")
|
||||||
|
tag := ra.GetString(":tag")
|
||||||
|
projectName, _ := utils.ParseRepository(repoName)
|
||||||
|
exist, err := ra.ProjectMgr.Exist(projectName)
|
||||||
|
if err != nil {
|
||||||
|
ra.HandleInternalServerError(fmt.Sprintf("failed to check the existence of project %s: %v",
|
||||||
|
projectName, err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !exist {
|
||||||
|
ra.HandleNotFound(fmt.Sprintf("project %s not found", projectName))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !ra.SecurityCtx.IsAuthenticated() {
|
||||||
|
ra.HandleUnauthorized()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !ra.SecurityCtx.HasAllPerm(projectName) {
|
||||||
|
ra.HandleForbidden(ra.SecurityCtx.GetUsername())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = TriggerImageScan(repoName, tag)
|
||||||
|
//TODO better check existence
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("Error while calling job service to trigger image scan: %v", err)
|
||||||
|
ra.HandleInternalServerError("Failed to scan image, please check log for details")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func getSignatures(repository, username string) (map[string]*notary.Target, error) {
|
func getSignatures(repository, username string) (map[string]*notary.Target, error) {
|
||||||
targets, err := notary.GetInternalTargets(config.InternalNotaryEndpoint(),
|
targets, err := notary.GetInternalTargets(config.InternalNotaryEndpoint(),
|
||||||
username, repository)
|
username, repository)
|
||||||
|
@ -18,6 +18,7 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
"sort"
|
"sort"
|
||||||
@ -26,10 +27,10 @@ import (
|
|||||||
"github.com/vmware/harbor/src/common/dao"
|
"github.com/vmware/harbor/src/common/dao"
|
||||||
"github.com/vmware/harbor/src/common/models"
|
"github.com/vmware/harbor/src/common/models"
|
||||||
"github.com/vmware/harbor/src/common/utils"
|
"github.com/vmware/harbor/src/common/utils"
|
||||||
|
registry_error "github.com/vmware/harbor/src/common/utils/error"
|
||||||
"github.com/vmware/harbor/src/common/utils/log"
|
"github.com/vmware/harbor/src/common/utils/log"
|
||||||
"github.com/vmware/harbor/src/common/utils/registry"
|
"github.com/vmware/harbor/src/common/utils/registry"
|
||||||
"github.com/vmware/harbor/src/common/utils/registry/auth"
|
"github.com/vmware/harbor/src/common/utils/registry/auth"
|
||||||
registry_error "github.com/vmware/harbor/src/common/utils/error"
|
|
||||||
"github.com/vmware/harbor/src/ui/config"
|
"github.com/vmware/harbor/src/ui/config"
|
||||||
"github.com/vmware/harbor/src/ui/projectmanager"
|
"github.com/vmware/harbor/src/ui/projectmanager"
|
||||||
)
|
)
|
||||||
@ -92,32 +93,9 @@ func TriggerReplication(policyID int64, repository string,
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
url := buildReplicationURL()
|
url := buildReplicationURL()
|
||||||
|
|
||||||
req, err := http.NewRequest("POST", url, bytes.NewBuffer(b))
|
return requestAsUI("POST", url, bytes.NewBuffer(b), http.StatusOK)
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
addAuthentication(req)
|
|
||||||
|
|
||||||
client := &http.Client{}
|
|
||||||
resp, err := client.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
|
|
||||||
if resp.StatusCode == http.StatusOK {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
b, err = ioutil.ReadAll(resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return fmt.Errorf("%d %s", resp.StatusCode, string(b))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetPoliciesByRepository returns policies according the repository
|
// GetPoliciesByRepository returns policies according the repository
|
||||||
@ -451,6 +429,11 @@ func initRegistryClient() (r *registry.Registry, err error) {
|
|||||||
return registryClient, nil
|
return registryClient, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func buildScanJobURL() string {
|
||||||
|
url := config.InternalJobServiceURL()
|
||||||
|
return fmt.Sprintf("%s/api/jobs/scan", url)
|
||||||
|
}
|
||||||
|
|
||||||
func buildReplicationURL() string {
|
func buildReplicationURL() string {
|
||||||
url := config.InternalJobServiceURL()
|
url := config.InternalJobServiceURL()
|
||||||
return fmt.Sprintf("%s/api/jobs/replication", url)
|
return fmt.Sprintf("%s/api/jobs/replication", url)
|
||||||
@ -535,3 +518,42 @@ func NewRepositoryClient(endpoint string, insecure bool, username, repository, s
|
|||||||
}
|
}
|
||||||
return client, nil
|
return client, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TriggerImageScan triggers an image scan job on jobservice.
|
||||||
|
func TriggerImageScan(repository string, tag string) error {
|
||||||
|
data := &models.ImageScanReq{
|
||||||
|
Repo: repository,
|
||||||
|
Tag: tag,
|
||||||
|
}
|
||||||
|
b, err := json.Marshal(&data)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
url := buildScanJobURL()
|
||||||
|
return requestAsUI("POST", url, bytes.NewBuffer(b), http.StatusOK)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do not use this when you want to handle the response
|
||||||
|
// TODO: add a response handler to replace expectSC *when needed*
|
||||||
|
func requestAsUI(method, url string, body io.Reader, expectSC int) error {
|
||||||
|
req, err := http.NewRequest(method, url, body)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
addAuthentication(req)
|
||||||
|
client := &http.Client{}
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if resp.StatusCode != expectSC {
|
||||||
|
b, err := ioutil.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return fmt.Errorf("Unexpected status code: %d, text: %s", resp.StatusCode, string(b))
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -76,6 +76,7 @@ func initRouters() {
|
|||||||
beego.Router("/api/repositories/*", &api.RepositoryAPI{}, "delete:Delete")
|
beego.Router("/api/repositories/*", &api.RepositoryAPI{}, "delete:Delete")
|
||||||
beego.Router("/api/repositories/*/tags/:tag", &api.RepositoryAPI{}, "delete:Delete")
|
beego.Router("/api/repositories/*/tags/:tag", &api.RepositoryAPI{}, "delete:Delete")
|
||||||
beego.Router("/api/repositories/*/tags", &api.RepositoryAPI{}, "get:GetTags")
|
beego.Router("/api/repositories/*/tags", &api.RepositoryAPI{}, "get:GetTags")
|
||||||
|
beego.Router("/api/repositories/*/tags/:tag/scan", &api.RepositoryAPI{}, "post:ScanImage")
|
||||||
beego.Router("/api/repositories/*/tags/:tag/manifest", &api.RepositoryAPI{}, "get:GetManifests")
|
beego.Router("/api/repositories/*/tags/:tag/manifest", &api.RepositoryAPI{}, "get:GetManifests")
|
||||||
beego.Router("/api/repositories/*/signatures", &api.RepositoryAPI{}, "get:GetSignatures")
|
beego.Router("/api/repositories/*/signatures", &api.RepositoryAPI{}, "get:GetSignatures")
|
||||||
beego.Router("/api/jobs/replication/", &api.RepJobAPI{}, "get:List")
|
beego.Router("/api/jobs/replication/", &api.RepJobAPI{}, "get:List")
|
||||||
|
Loading…
Reference in New Issue
Block a user