Ai
1 Star 0 Fork 1

mysnapcore/mysnapd

forked from tupelo-shen/mysnapd 
加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
api_apps.go 7.73 KB
一键复制 编辑 原始数据 按行查看 历史
tupelo-shen 提交于 2022-11-08 22:56 +08:00 . fix: daemon commit
// -*- Mode: Go; indent-tabs-mode: t -*-
/*
* Copyright (C) 2015-2020 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 (
"encoding/json"
"fmt"
"net/http"
"sort"
"strconv"
"strings"
"gitee.com/mysnapcore/mysnapd/client/clientutil"
"gitee.com/mysnapcore/mysnapd/overlord/auth"
"gitee.com/mysnapcore/mysnapd/overlord/servicestate"
"gitee.com/mysnapcore/mysnapd/overlord/state"
"gitee.com/mysnapcore/mysnapd/progress"
"gitee.com/mysnapcore/mysnapd/snap"
"gitee.com/mysnapcore/mysnapd/strutil"
)
var (
appsCmd = &Command{
Path: "/v2/apps",
GET: getAppsInfo,
POST: postApps,
ReadAccess: openAccess{},
WriteAccess: authenticatedAccess{},
}
logsCmd = &Command{
Path: "/v2/logs",
GET: getLogs,
ReadAccess: authenticatedAccess{Polkit: polkitActionManage},
}
)
func getAppsInfo(c *Command, r *http.Request, user *auth.UserState) Response {
query := r.URL.Query()
opts := appInfoOptions{}
switch sel := query.Get("select"); sel {
case "":
// nothing to do
case "service":
opts.service = true
default:
return BadRequest("invalid select parameter: %q", sel)
}
appInfos, rspe := appInfosFor(c.d.overlord.State(), strutil.CommaSeparatedList(query.Get("names")), opts)
if rspe != nil {
return rspe
}
sd := servicestate.NewStatusDecorator(progress.Null)
clientAppInfos, err := clientutil.ClientAppInfosFromSnapAppInfos(appInfos, sd)
if err != nil {
return InternalError("%v", err)
}
return SyncResponse(clientAppInfos)
}
type appInfoOptions struct {
service bool
}
func (opts appInfoOptions) String() string {
if opts.service {
return "service"
}
return "app"
}
// appInfosFor returns a sorted list apps described by names.
//
// * If names is empty, returns all apps of the wanted kinds (which
// could be an empty list).
// * An element of names can be a snap name, in which case all apps
// from the snap of the wanted kind are included in the result (and
// it's an error if the snap has no apps of the wanted kind).
// * An element of names can instead be snap.app, in which case that app is
// included in the result (and it's an error if the snap and app don't
// both exist, or if the app is not a wanted kind)
// On error an appropriate *apiError is returned; a nil *apiError means
// no error.
//
// It's a programming error to call this with wanted having neither
// services nor commands set.
func appInfosFor(st *state.State, names []string, opts appInfoOptions) ([]*snap.AppInfo, *apiError) {
snapNames := make(map[string]bool)
requested := make(map[string]bool)
for _, name := range names {
requested[name] = true
name, _ = splitAppName(name)
snapNames[name] = true
}
snaps, err := allLocalSnapInfos(st, false, snapNames)
if err != nil {
return nil, InternalError("cannot list local snaps! %v", err)
}
found := make(map[string]bool)
appInfos := make([]*snap.AppInfo, 0, len(requested))
for _, snp := range snaps {
snapName := snp.info.InstanceName()
apps := make([]*snap.AppInfo, 0, len(snp.info.Apps))
for _, app := range snp.info.Apps {
if !opts.service || app.IsService() {
apps = append(apps, app)
}
}
if len(apps) == 0 && requested[snapName] {
return nil, AppNotFound("snap %q has no %ss", snapName, opts)
}
includeAll := len(requested) == 0 || requested[snapName]
if includeAll {
// want all services in a snap
found[snapName] = true
}
for _, app := range apps {
appName := snapName + "." + app.Name
if includeAll || requested[appName] {
appInfos = append(appInfos, app)
found[appName] = true
}
}
}
for k := range requested {
if !found[k] {
if snapNames[k] {
return nil, SnapNotFound(k, fmt.Errorf("snap %q not found", k))
} else {
snap, app := splitAppName(k)
return nil, AppNotFound("snap %q has no %s %q", snap, opts, app)
}
}
}
sort.Sort(snap.AppInfoBySnapApp(appInfos))
return appInfos, nil
}
// this differs from snap.SplitSnapApp in the handling of the
// snap-only case:
// snap.SplitSnapApp("foo") is ("foo", "foo"),
// splitAppName("foo") is ("foo", "").
func splitAppName(s string) (snap, app string) {
if idx := strings.IndexByte(s, '.'); idx > -1 {
return s[:idx], s[idx+1:]
}
return s, ""
}
func getLogs(c *Command, r *http.Request, user *auth.UserState) Response {
query := r.URL.Query()
n := 10
if s := query.Get("n"); s != "" {
m, err := strconv.ParseInt(s, 0, 32)
if err != nil {
return BadRequest(`invalid value for n: %q: %v`, s, err)
}
n = int(m)
}
follow := false
if s := query.Get("follow"); s != "" {
f, err := strconv.ParseBool(s)
if err != nil {
return BadRequest(`invalid value for follow: %q: %v`, s, err)
}
follow = f
}
// only services have logs for now
opts := appInfoOptions{service: true}
appInfos, rspe := appInfosFor(c.d.overlord.State(), strutil.CommaSeparatedList(query.Get("names")), opts)
if rspe != nil {
return rspe
}
if len(appInfos) == 0 {
return AppNotFound("no matching services")
}
reader, err := servicestate.LogReader(appInfos, n, follow)
if err != nil {
return InternalError("cannot get logs: %v", err)
}
return &journalLineReaderSeqResponse{
ReadCloser: reader,
follow: follow,
}
}
var servicestateControl = servicestate.Control
func postApps(c *Command, r *http.Request, user *auth.UserState) Response {
var inst servicestate.Instruction
decoder := json.NewDecoder(r.Body)
if err := decoder.Decode(&inst); err != nil {
return BadRequest("cannot decode request body into service operation: %v", err)
}
// XXX: decoder.More()
if len(inst.Names) == 0 {
// on POST, don't allow empty to mean all
return BadRequest("cannot perform operation on services without a list of services to operate on")
}
st := c.d.overlord.State()
appInfos, rspe := appInfosFor(st, inst.Names, appInfoOptions{service: true})
if rspe != nil {
return rspe
}
if len(appInfos) == 0 {
// can't happen: appInfosFor with a non-empty list of services
// shouldn't ever return an empty appInfos with no error response
return InternalError("no services found")
}
// do not pass flags - only create service-control tasks, do not create
// exec-command tasks for old snapd. These are not needed since we are
// handling momentary snap service commands.
st.Lock()
defer st.Unlock()
tss, err := servicestateControl(st, appInfos, &inst, nil, nil)
if err != nil {
// TODO: use errToResponse here too and introduce a proper error kind ?
if _, ok := err.(servicestate.ServiceActionConflictError); ok {
return Conflict(err.Error())
}
return BadRequest(err.Error())
}
// names received in the request can be snap or snap.app, we need to
// extract the actual snap names before associating them with a change
chg := newChange(st, "service-control", "Running service command", tss, namesToSnapNames(&inst))
st.EnsureBefore(0)
return AsyncResponse(nil, chg.ID())
}
func namesToSnapNames(inst *servicestate.Instruction) []string {
seen := make(map[string]struct{}, len(inst.Names))
for _, snapOrSnapDotApp := range inst.Names {
snapName, _ := snap.SplitSnapApp(snapOrSnapDotApp)
seen[snapName] = struct{}{}
}
names := make([]string, 0, len(seen))
for k := range seen {
names = append(names, k)
}
// keep stable ordering
sort.Strings(names)
return names
}
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
Go
1
https://gitee.com/mysnapcore/mysnapd.git
git@gitee.com:mysnapcore/mysnapd.git
mysnapcore
mysnapd
mysnapd
v0.1.0

搜索帮助