blob: 79b4c6719db17ed1ed7f898c4c9e8d928bdbf022 [file] [log] [blame]
// Copyright 2017 The LUCI Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package settings contains definition of global CIPD backend settings.
//
// These are settings that are usually set only once after the initial service
// deployment. They are exposed through Admin portal interface.
package settings
import (
"context"
"go.chromium.org/luci/common/errors"
"go.chromium.org/luci/common/retry/transient"
"go.chromium.org/luci/grpc/grpcutil"
"go.chromium.org/luci/server/portal"
"go.chromium.org/luci/server/settings"
api "go.chromium.org/luci/cipd/api/cipd/v1"
"go.chromium.org/luci/cipd/appengine/impl/gs"
)
const settingsKey = "cipd"
// Settings contain CIPD backend settings.
type Settings struct {
// StorageGSPath is GS path in a form of /bucket/path to the root of the
// content-addressable storage area in Google Storage.
//
// The files will be stored as /storage_gs_path/hash_algo/hex_digest.
StorageGSPath string `json:"storage_gs_path"`
// TempGSPath is GS path in a form of /bucket/path to the root of the storage
// area for pending uploads.
//
// It contains unverified files uploaded by clients before they pass the
// hash verification check and copied to the CAS storage area.
TempGSPath string `json:"temp_gs_path"`
// SignAs is a name of a service account whose private key to use for
// generating signed Google Storage URLs (through signBlob IAM API) instead of
// the default service account.
//
// This is mostly useful on dev_server running under developer's account. It
// doesn't have private keys for signing, but can still call signBlob API.
SignAs string `json:"sign_as"`
}
// Get returns the settings from the local cache, checking they are populated.
//
// Returns grpc-annotated Internal error if something is wrong.
func Get(ctx context.Context) (*Settings, error) {
s := &Settings{}
if err := settings.Get(ctx, settingsKey, s); err != nil && err != settings.ErrNoSettings {
return nil, errors.Annotate(err, "failed to fetch settings").
Tag(grpcutil.InternalTag, transient.Tag).Err()
}
if s.StorageGSPath == "" || s.TempGSPath == "" {
return nil, errors.Reason("the backend is not configured").
Tag(grpcutil.InternalTag).Err()
}
return s, nil
}
// ObjectPath constructs a path to the object in the Google Storage, starting
// from StorageGSPath root.
func (s *Settings) ObjectPath(obj *api.ObjectRef) string {
return s.StorageGSPath + "/" + obj.HashAlgo.String() + "/" + obj.HexDigest
}
type settingsPage struct {
portal.BasePage
}
func (*settingsPage) Title(context.Context) (string, error) {
return "CIPD settings", nil
}
func (*settingsPage) Fields(context.Context) ([]portal.Field, error) {
return []portal.Field{
{
ID: "StorageGSPath",
Title: "Content store path",
Type: portal.FieldText,
Help: "<p>A Google Storage path in a form of /bucket/path to the root of the " +
"content-addressable storage area in Google Storage. The files will be " +
"stored as /storage_gs_path/hash_algo/hex_digest.</p>",
Placeholder: "/<bucket>/<path>",
Validator: func(p string) error {
if p == "" {
return nil
}
return gs.ValidatePath(p)
},
},
{
ID: "TempGSPath",
Title: "Temp path",
Type: portal.FieldText,
Help: "<p>A Google Storage path in a form of /bucket/path to the root of the " +
"storage area for pending uploads. It contains unverified files uploaded " +
"by clients before they pass the hash verification check and copied to " +
"the CAS storage area.</p>",
Placeholder: "/<bucket>/<path>",
Validator: func(p string) error {
if p == "" {
return nil
}
return gs.ValidatePath(p)
},
},
{
ID: "SignAs",
Title: "Sign as",
Type: portal.FieldText,
Help: "<p>A name of a service account whose private key to use for " +
"generating signed Google Storage URLs (through signBlob IAM API) " +
"instead of the default service account. This is mostly useful on " +
"dev_server running under developer's account. It doesn't have private " +
"keys for signing, but can still call signBlob API.</p>",
Placeholder: "<email>",
},
}, nil
}
func (*settingsPage) ReadSettings(ctx context.Context) (map[string]string, error) {
c := &Settings{}
if err := settings.GetUncached(ctx, settingsKey, c); err != nil && err != settings.ErrNoSettings {
return nil, err
}
return map[string]string{
"StorageGSPath": c.StorageGSPath,
"TempGSPath": c.TempGSPath,
"SignAs": c.SignAs,
}, nil
}
func (*settingsPage) WriteSettings(ctx context.Context, values map[string]string, who, why string) error {
return settings.SetIfChanged(ctx, settingsKey, &Settings{
StorageGSPath: values["StorageGSPath"],
TempGSPath: values["TempGSPath"],
SignAs: values["SignAs"],
}, who, why)
}
func init() {
portal.RegisterPage(settingsKey, &settingsPage{})
}