mirror of
https://github.com/goharbor/harbor.git
synced 2024-11-23 10:45:45 +01:00
bypass docker client retry on push in readonly mode (#4524)
This commit is contained in:
parent
24dfad4afe
commit
892d9bf023
@ -42,6 +42,7 @@ func filter(req *http.Request, resp http.ResponseWriter) {
|
|||||||
}
|
}
|
||||||
if matchRepoTagDelete(req) {
|
if matchRepoTagDelete(req) {
|
||||||
resp.WriteHeader(http.StatusServiceUnavailable)
|
resp.WriteHeader(http.StatusServiceUnavailable)
|
||||||
|
resp.Write([]byte("The system is in read only mode. Any modification is not prohibited."))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -195,8 +195,10 @@ func TestCopyResp(t *testing.T) {
|
|||||||
|
|
||||||
func TestMarshalError(t *testing.T) {
|
func TestMarshalError(t *testing.T) {
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
js := marshalError("Not Found")
|
js1 := marshalError("PROJECT_POLICY_VIOLATION", "Not Found")
|
||||||
assert.Equal("{\"errors\":[{\"code\":\"PROJECT_POLICY_VIOLATION\",\"message\":\"Not Found\",\"detail\":\"Not Found\"}]}", js)
|
assert.Equal("{\"errors\":[{\"code\":\"PROJECT_POLICY_VIOLATION\",\"message\":\"Not Found\",\"detail\":\"Not Found\"}]}", js1)
|
||||||
|
js2 := marshalError("DENIED", "The action is denied")
|
||||||
|
assert.Equal("{\"errors\":[{\"code\":\"DENIED\",\"message\":\"The action is denied\",\"detail\":\"The action is denied\"}]}", js2)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestIsDigest(t *testing.T) {
|
func TestIsDigest(t *testing.T) {
|
||||||
|
@ -35,7 +35,7 @@ const (
|
|||||||
var rec *httptest.ResponseRecorder
|
var rec *httptest.ResponseRecorder
|
||||||
|
|
||||||
// NotaryEndpoint , exported for testing.
|
// NotaryEndpoint , exported for testing.
|
||||||
var NotaryEndpoint =""
|
var NotaryEndpoint = ""
|
||||||
|
|
||||||
// MatchPullManifest checks if the request looks like a request to pull manifest. If it is returns the image and tag/sha256 digest as 2nd and 3rd return values
|
// MatchPullManifest checks if the request looks like a request to pull manifest. If it is returns the image and tag/sha256 digest as 2nd and 3rd return values
|
||||||
func MatchPullManifest(req *http.Request) (bool, string, string) {
|
func MatchPullManifest(req *http.Request) (bool, string, string) {
|
||||||
@ -123,20 +123,20 @@ func (uh urlHandler) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
|||||||
if flag {
|
if flag {
|
||||||
components := strings.SplitN(repository, "/", 2)
|
components := strings.SplitN(repository, "/", 2)
|
||||||
if len(components) < 2 {
|
if len(components) < 2 {
|
||||||
http.Error(rw, marshalError(fmt.Sprintf("Bad repository name: %s", repository)), http.StatusBadRequest)
|
http.Error(rw, marshalError("PROJECT_POLICY_VIOLATION", fmt.Sprintf("Bad repository name: %s", repository)), http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
client, err := uiutils.NewRepositoryClientForUI(tokenUsername, repository)
|
client, err := uiutils.NewRepositoryClientForUI(tokenUsername, repository)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("Error creating repository Client: %v", err)
|
log.Errorf("Error creating repository Client: %v", err)
|
||||||
http.Error(rw, marshalError(fmt.Sprintf("Failed due to internal Error: %v", err)), http.StatusInternalServerError)
|
http.Error(rw, marshalError("PROJECT_POLICY_VIOLATION", fmt.Sprintf("Failed due to internal Error: %v", err)), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
digest, _, err := client.ManifestExist(reference)
|
digest, _, err := client.ManifestExist(reference)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("Failed to get digest for reference: %s, error: %v", reference, err)
|
log.Errorf("Failed to get digest for reference: %s, error: %v", reference, err)
|
||||||
http.Error(rw, marshalError(fmt.Sprintf("Failed due to internal Error: %v", err)), http.StatusInternalServerError)
|
http.Error(rw, marshalError("PROJECT_POLICY_VIOLATION", fmt.Sprintf("Failed due to internal Error: %v", err)), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -161,7 +161,7 @@ type readonlyHandler struct {
|
|||||||
func (rh readonlyHandler) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
func (rh readonlyHandler) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
||||||
if config.ReadOnly() {
|
if config.ReadOnly() {
|
||||||
if req.Method == http.MethodDelete || req.Method == http.MethodPost || req.Method == http.MethodPatch {
|
if req.Method == http.MethodDelete || req.Method == http.MethodPost || req.Method == http.MethodPatch {
|
||||||
http.Error(rw, "Upload/Delete is prohibited in read only mode.", http.StatusServiceUnavailable)
|
http.Error(rw, marshalError("DENIED", "The system is in read only mode. Any modification is not prohibited."), http.StatusForbidden)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -241,12 +241,12 @@ func (cth contentTrustHandler) ServeHTTP(rw http.ResponseWriter, req *http.Reque
|
|||||||
}
|
}
|
||||||
match, err := matchNotaryDigest(img)
|
match, err := matchNotaryDigest(img)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(rw, marshalError("Failed in communication with Notary please check the log"), http.StatusInternalServerError)
|
http.Error(rw, marshalError("PROJECT_POLICY_VIOLATION", "Failed in communication with Notary please check the log"), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if !match {
|
if !match {
|
||||||
log.Debugf("digest mismatch, failing the response.")
|
log.Debugf("digest mismatch, failing the response.")
|
||||||
http.Error(rw, marshalError("The image is not signed in Notary."), http.StatusPreconditionFailed)
|
http.Error(rw, marshalError("PROJECT_POLICY_VIOLATION", "The image is not signed in Notary."), http.StatusPreconditionFailed)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
cth.next.ServeHTTP(rw, req)
|
cth.next.ServeHTTP(rw, req)
|
||||||
@ -275,19 +275,19 @@ func (vh vulnerableHandler) ServeHTTP(rw http.ResponseWriter, req *http.Request)
|
|||||||
overview, err := dao.GetImgScanOverview(img.digest)
|
overview, err := dao.GetImgScanOverview(img.digest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("failed to get ImgScanOverview with repo: %s, reference: %s, digest: %s. Error: %v", img.repository, img.reference, img.digest, err)
|
log.Errorf("failed to get ImgScanOverview with repo: %s, reference: %s, digest: %s. Error: %v", img.repository, img.reference, img.digest, err)
|
||||||
http.Error(rw, marshalError("Failed to get ImgScanOverview."), http.StatusPreconditionFailed)
|
http.Error(rw, marshalError("PROJECT_POLICY_VIOLATION", "Failed to get ImgScanOverview."), http.StatusPreconditionFailed)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// severity is 0 means that the image fails to scan or not scanned successfully.
|
// severity is 0 means that the image fails to scan or not scanned successfully.
|
||||||
if overview == nil || overview.Sev == 0 {
|
if overview == nil || overview.Sev == 0 {
|
||||||
log.Debugf("cannot get the image scan overview info, failing the response.")
|
log.Debugf("cannot get the image scan overview info, failing the response.")
|
||||||
http.Error(rw, marshalError("Cannot get the image severity."), http.StatusPreconditionFailed)
|
http.Error(rw, marshalError("PROJECT_POLICY_VIOLATION", "Cannot get the image severity."), http.StatusPreconditionFailed)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
imageSev := overview.Sev
|
imageSev := overview.Sev
|
||||||
if imageSev >= int(projectVulnerableSeverity) {
|
if imageSev >= int(projectVulnerableSeverity) {
|
||||||
log.Debugf("the image severity: %q is higher then project setting: %q, failing the response.", models.Severity(imageSev), projectVulnerableSeverity)
|
log.Debugf("the image severity: %q is higher then project setting: %q, failing the response.", models.Severity(imageSev), projectVulnerableSeverity)
|
||||||
http.Error(rw, marshalError(fmt.Sprintf("The severity of vulnerability of the image: %q is equal or higher than the threshold in project setting: %q.", models.Severity(imageSev), projectVulnerableSeverity)), http.StatusPreconditionFailed)
|
http.Error(rw, marshalError("PROJECT_POLICY_VIOLATION", fmt.Sprintf("The severity of vulnerability of the image: %q is equal or higher than the threshold in project setting: %q.", models.Severity(imageSev), projectVulnerableSeverity)), http.StatusPreconditionFailed)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
vh.next.ServeHTTP(rw, req)
|
vh.next.ServeHTTP(rw, req)
|
||||||
@ -340,12 +340,12 @@ func copyResp(rec *httptest.ResponseRecorder, rw http.ResponseWriter) {
|
|||||||
rw.Write(rec.Body.Bytes())
|
rw.Write(rec.Body.Bytes())
|
||||||
}
|
}
|
||||||
|
|
||||||
func marshalError(msg string) string {
|
func marshalError(code, msg string) string {
|
||||||
var tmpErrs struct {
|
var tmpErrs struct {
|
||||||
Errors []JSONError `json:"errors,omitempty"`
|
Errors []JSONError `json:"errors,omitempty"`
|
||||||
}
|
}
|
||||||
tmpErrs.Errors = append(tmpErrs.Errors, JSONError{
|
tmpErrs.Errors = append(tmpErrs.Errors, JSONError{
|
||||||
Code: "PROJECT_POLICY_VIOLATION",
|
Code: code,
|
||||||
Message: msg,
|
Message: msg,
|
||||||
Detail: msg,
|
Detail: msg,
|
||||||
})
|
})
|
||||||
|
Loading…
Reference in New Issue
Block a user