provide POST api/repostitores/xxx/tags/xxx/scan to trigger image scan

This commit is contained in:
Tan Jiang 2017-06-15 20:23:55 +08:00
parent 15384317e0
commit 41346fe8c0
5 changed files with 89 additions and 34 deletions

View File

@ -80,3 +80,9 @@ type ComponentsOverviewEntry struct {
Sev int `json:"severity"`
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"`
}

View File

@ -31,20 +31,15 @@ type ImageScanJob struct {
jobBaseAPI
}
type imageScanReq struct {
Repo string `json:"repository"`
Tag string `json:"tag"`
}
// Prepare ...
func (isj *ImageScanJob) Prepare() {
//TODO:add authenticate to check secret when integrate with UI API.
//isj.authenticate()
//TODO Uncomment to enable security check.
// isj.authenticate()
}
// Post creates a scanner job and hand it to statemachine.
func (isj *ImageScanJob) Post() {
var data imageScanReq
var data models.ImageScanReq
isj.DecodeJSONReq(&data)
log.Debugf("data: %+v", data)
regURL, err := config.LocalRegURL()

View File

@ -610,7 +610,6 @@ func (ra *RepositoryAPI) GetTopRepos() {
//GetSignatures returns signatures of a repository
func (ra *RepositoryAPI) GetSignatures() {
repoName := ra.GetString(":splat")
targets, err := notary.GetInternalTargets(config.InternalNotaryEndpoint(),
ra.SecurityCtx.GetUsername(), repoName)
if err != nil {
@ -621,6 +620,38 @@ func (ra *RepositoryAPI) GetSignatures() {
ra.ServeJSON()
}
//ScanImage handles
func (ra *RepositoryAPI) ScanImage() {
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) {
targets, err := notary.GetInternalTargets(config.InternalNotaryEndpoint(),
username, repository)

View File

@ -18,6 +18,7 @@ import (
"bytes"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net/http"
"sort"
@ -26,10 +27,10 @@ import (
"github.com/vmware/harbor/src/common/dao"
"github.com/vmware/harbor/src/common/models"
"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/registry"
"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/projectmanager"
)
@ -92,32 +93,9 @@ func TriggerReplication(policyID int64, repository string,
if err != nil {
return err
}
url := buildReplicationURL()
req, err := http.NewRequest("POST", url, bytes.NewBuffer(b))
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))
return requestAsUI("POST", url, bytes.NewBuffer(b), http.StatusOK)
}
// GetPoliciesByRepository returns policies according the repository
@ -451,6 +429,11 @@ func initRegistryClient() (r *registry.Registry, err error) {
return registryClient, nil
}
func buildScanJobURL() string {
url := config.InternalJobServiceURL()
return fmt.Sprintf("%s/api/jobs/scan", url)
}
func buildReplicationURL() string {
url := config.InternalJobServiceURL()
return fmt.Sprintf("%s/api/jobs/replication", url)
@ -535,3 +518,42 @@ func NewRepositoryClient(endpoint string, insecure bool, username, repository, s
}
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
}

View File

@ -76,6 +76,7 @@ func initRouters() {
beego.Router("/api/repositories/*", &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/:tag/scan", &api.RepositoryAPI{}, "post:ScanImage")
beego.Router("/api/repositories/*/tags/:tag/manifest", &api.RepositoryAPI{}, "get:GetManifests")
beego.Router("/api/repositories/*/signatures", &api.RepositoryAPI{}, "get:GetSignatures")
beego.Router("/api/jobs/replication/", &api.RepJobAPI{}, "get:List")