Ai
1 Star 0 Fork 1

mysnapcore/mysnapd

forked from tupelo-shen/mysnapd 
加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
errors.go 10.30 KB
一键复制 编辑 原始数据 按行查看 历史
tupelo-shen 提交于 2022-11-08 22:56 +08:00 . fix: daemon commit
// -*- Mode: Go; indent-tabs-mode: t -*-
/*
* Copyright (C) 2015-2021 Canonical Ltd
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package daemon
import (
"errors"
"fmt"
"net"
"net/http"
"gitee.com/mysnapcore/mysnapd/arch"
"gitee.com/mysnapcore/mysnapd/client"
"gitee.com/mysnapcore/mysnapd/overlord/servicestate"
"gitee.com/mysnapcore/mysnapd/overlord/snapstate"
"gitee.com/mysnapcore/mysnapd/snap"
"gitee.com/mysnapcore/mysnapd/store"
)
// apiError represents an error meant for returning to the client.
// It can serialize itself to our standard JSON response format.
type apiError struct {
// Status is the error HTTP status code.
Status int
Message string
// Kind is the error kind. See client/errors.go
Kind client.ErrorKind
Value errorValue
}
func (ae *apiError) Error() string {
kindOrStatus := "api"
if ae.Kind != "" {
kindOrStatus = fmt.Sprintf("api: %s", ae.Kind)
} else if ae.Status != 400 {
kindOrStatus = fmt.Sprintf("api %d", ae.Status)
}
return fmt.Sprintf("%s (%s)", ae.Message, kindOrStatus)
}
func (ae *apiError) JSON() *respJSON {
return &respJSON{
Status: ae.Status,
Type: ResponseTypeError,
Result: &errorResult{
Message: ae.Message,
Kind: ae.Kind,
Value: ae.Value,
},
}
}
func (ae *apiError) ServeHTTP(w http.ResponseWriter, r *http.Request) {
ae.JSON().ServeHTTP(w, r)
}
// check it implements StructuredResponse
var _ StructuredResponse = (*apiError)(nil)
type errorValue interface{}
type errorResult struct {
Message string `json:"message"` // note no omitempty
// Kind is the error kind. See client/errors.go
Kind client.ErrorKind `json:"kind,omitempty"`
Value errorValue `json:"value,omitempty"`
}
// errorResponder is a callable that produces an error Response.
// e.g., InternalError("something broke: %v", err), etc.
type errorResponder func(string, ...interface{}) *apiError
// makeErrorResponder builds an errorResponder from the given error status.
func makeErrorResponder(status int) errorResponder {
return func(format string, v ...interface{}) *apiError {
var msg string
if len(v) == 0 {
msg = format
} else {
msg = fmt.Sprintf(format, v...)
}
var kind client.ErrorKind
if status == 401 || status == 403 {
kind = client.ErrorKindLoginRequired
}
return &apiError{
Status: status,
Message: msg,
Kind: kind,
}
}
}
// standard error responses
var (
Unauthorized = makeErrorResponder(401)
NotFound = makeErrorResponder(404)
BadRequest = makeErrorResponder(400)
MethodNotAllowed = makeErrorResponder(405)
InternalError = makeErrorResponder(500)
NotImplemented = makeErrorResponder(501)
Forbidden = makeErrorResponder(403)
Conflict = makeErrorResponder(409)
)
// BadQuery is an error responder used when a bad query was
// provided to the store.
func BadQuery() *apiError {
return &apiError{
Status: 400,
Message: "bad query",
Kind: client.ErrorKindBadQuery,
}
}
// SnapNotFound is an error responder used when an operation is
// requested on a snap that doesn't exist.
func SnapNotFound(snapName string, err error) *apiError {
return &apiError{
Status: 404,
Message: err.Error(),
Kind: client.ErrorKindSnapNotFound,
Value: snapName,
}
}
// SnapRevisionNotAvailable is an error responder used when an
// operation is requested for which no revivision can be found
// in the given context (e.g. request an install from a stable
// channel when this channel is empty).
func SnapRevisionNotAvailable(snapName string, rnaErr *store.RevisionNotAvailableError) *apiError {
var value interface{} = snapName
kind := client.ErrorKindSnapRevisionNotAvailable
msg := rnaErr.Error()
if len(rnaErr.Releases) != 0 && rnaErr.Channel != "" {
thisArch := arch.DpkgArchitecture()
values := map[string]interface{}{
"snap-name": snapName,
"action": rnaErr.Action,
"channel": rnaErr.Channel,
"architecture": thisArch,
}
archOK := false
releases := make([]map[string]interface{}, 0, len(rnaErr.Releases))
for _, c := range rnaErr.Releases {
if c.Architecture == thisArch {
archOK = true
}
releases = append(releases, map[string]interface{}{
"architecture": c.Architecture,
"channel": c.Name,
})
}
// we return all available releases (arch x channel)
// as reported in the store error, but we hint with
// the error kind whether there was anything at all
// available for this architecture
if archOK {
kind = client.ErrorKindSnapChannelNotAvailable
msg = "no snap revision on specified channel"
} else {
kind = client.ErrorKindSnapArchitectureNotAvailable
msg = "no snap revision on specified architecture"
}
values["releases"] = releases
value = values
}
return &apiError{
Status: 404,
Message: msg,
Kind: kind,
Value: value,
}
}
// SnapChangeConflict is an error responder used when an operation would
// conflict with another ongoing change.
func SnapChangeConflict(cce *snapstate.ChangeConflictError) *apiError {
value := map[string]interface{}{}
if cce.Snap != "" {
value["snap-name"] = cce.Snap
}
if cce.ChangeKind != "" {
value["change-kind"] = cce.ChangeKind
}
return &apiError{
Status: 409,
Message: cce.Error(),
Kind: client.ErrorKindSnapChangeConflict,
Value: value,
}
}
// QuotaChangeConflict is an error responder used when an operation would
// conflict with another ongoing change.
func QuotaChangeConflict(qce *servicestate.QuotaChangeConflictError) *apiError {
value := map[string]interface{}{}
if qce.Quota != "" {
value["quota-name"] = qce.Quota
}
if qce.ChangeKind != "" {
value["change-kind"] = qce.ChangeKind
}
return &apiError{
Status: 409,
Message: qce.Error(),
Kind: client.ErrorKindQuotaChangeConflict,
Value: value,
}
}
// InsufficientSpace is an error responder used when an operation cannot
// be performed due to low disk space.
func InsufficientSpace(dserr *snapstate.InsufficientSpaceError) *apiError {
value := map[string]interface{}{}
if len(dserr.Snaps) > 0 {
value["snap-names"] = dserr.Snaps
}
if dserr.ChangeKind != "" {
value["change-kind"] = dserr.ChangeKind
}
return &apiError{
Status: 507,
Message: dserr.Error(),
Kind: client.ErrorKindInsufficientDiskSpace,
Value: value,
}
}
// AppNotFound is an error responder used when an operation is
// requested on a app that doesn't exist.
func AppNotFound(format string, v ...interface{}) *apiError {
return &apiError{
Status: 404,
Message: fmt.Sprintf(format, v...),
Kind: client.ErrorKindAppNotFound,
}
}
// AuthCancelled is an error responder used when a user cancelled
// the auth process.
func AuthCancelled(format string, v ...interface{}) *apiError {
return &apiError{
Status: 403,
Message: fmt.Sprintf(format, v...),
Kind: client.ErrorKindAuthCancelled,
}
}
// InterfacesUnchanged is an error responder used when an operation
// that would normally change interfaces finds it has nothing to do
func InterfacesUnchanged(format string, v ...interface{}) *apiError {
return &apiError{
Status: 400,
Message: fmt.Sprintf(format, v...),
Kind: client.ErrorKindInterfacesUnchanged,
}
}
func errToResponse(err error, snaps []string, fallback errorResponder, format string, v ...interface{}) *apiError {
var kind client.ErrorKind
var snapName string
switch err {
case store.ErrSnapNotFound:
switch len(snaps) {
case 1:
return SnapNotFound(snaps[0], err)
// store.ErrSnapNotFound should only be returned for individual
// snap queries; in all other cases something's wrong
case 0:
return InternalError("store.SnapNotFound with no snap given")
default:
return InternalError("store.SnapNotFound with %d snaps", len(snaps))
}
case store.ErrNoUpdateAvailable:
kind = client.ErrorKindSnapNoUpdateAvailable
case store.ErrLocalSnap:
kind = client.ErrorKindSnapLocal
default:
handled := true
switch err := err.(type) {
case *store.RevisionNotAvailableError:
// store.ErrRevisionNotAvailable should only be returned for
// individual snap queries; in all other cases something's wrong
switch len(snaps) {
case 1:
return SnapRevisionNotAvailable(snaps[0], err)
case 0:
return InternalError("store.RevisionNotAvailable with no snap given")
default:
return InternalError("store.RevisionNotAvailable with %d snaps", len(snaps))
}
case *snap.AlreadyInstalledError:
kind = client.ErrorKindSnapAlreadyInstalled
snapName = err.Snap
case *snap.NotInstalledError:
kind = client.ErrorKindSnapNotInstalled
snapName = err.Snap
case *servicestate.QuotaChangeConflictError:
return QuotaChangeConflict(err)
case *snapstate.SnapNeedsDevModeError:
kind = client.ErrorKindSnapNeedsDevMode
snapName = err.Snap
case *snapstate.SnapNeedsClassicError:
kind = client.ErrorKindSnapNeedsClassic
snapName = err.Snap
case *snapstate.SnapNeedsClassicSystemError:
kind = client.ErrorKindSnapNeedsClassicSystem
snapName = err.Snap
case *snapstate.SnapNotClassicError:
kind = client.ErrorKindSnapNotClassic
snapName = err.Snap
case *snapstate.InsufficientSpaceError:
return InsufficientSpace(err)
case net.Error:
if err.Timeout() {
kind = client.ErrorKindNetworkTimeout
} else {
handled = false
}
case *store.SnapActionError:
// we only handle a few specific cases
_, _, e := err.SingleOpError()
if e != nil {
// 👉😎👉
return errToResponse(e, snaps, fallback, format)
}
handled = false
default:
// support wrapped errors
switch {
case errors.Is(err, &snapstate.ChangeConflictError{}):
var conflErr *snapstate.ChangeConflictError
if errors.As(err, &conflErr) {
return SnapChangeConflict(conflErr)
}
}
handled = false
}
if !handled {
v = append(v, err)
return fallback(format, v...)
}
}
return &apiError{
Status: 400,
Message: err.Error(),
Kind: kind,
Value: snapName,
}
}
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
Go
1
https://gitee.com/mysnapcore/mysnapd.git
git@gitee.com:mysnapcore/mysnapd.git
mysnapcore
mysnapd
mysnapd
v0.1.0

搜索帮助