blob: 19b025d64b222033102c3be9fcf648246dcefa4e [file] [log] [blame]
// Copyright 2019 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 projectidentity
import (
"context"
"errors"
"go.chromium.org/luci/common/logging"
"go.chromium.org/luci/common/retry/transient"
ds "go.chromium.org/luci/gae/service/datastore"
)
var (
// ErrNotFound indicates that the entity which was queried does not exist in the storage.
ErrNotFound = errors.New("not found")
)
// projectIdentities is the default storage for all scoped identities.
var projectIdentities = &persistentStorage{}
// ProjectIdentities returns the global scoped identity storage.
func ProjectIdentities(_ context.Context) Storage {
return projectIdentities
}
// Storage interface declares methods for the scoped identity storage.
type Storage interface {
// Create an identity or update if it already exists.
Create(c context.Context, identity *ProjectIdentity) (*ProjectIdentity, error)
// Update an identity in the storage.
Update(c context.Context, identity *ProjectIdentity) (*ProjectIdentity, error)
// Delete an identity from the storage.
Delete(c context.Context, identity *ProjectIdentity) error
// LookupByProject performs a lookup by project name.
LookupByProject(c context.Context, project string) (*ProjectIdentity, error)
}
// ProjectIdentity defines a scoped identity in the storage.
type ProjectIdentity struct {
_kind string `gae:"$kind,ScopedIdentity"`
Project string `gae:"$id"`
Email string
}
// persistentStorage implements ScopedIdentityManager.
type persistentStorage struct {
}
// lookup reads an identity from the storage based on what fields are set in the identity struct.
func (s *persistentStorage) lookup(c context.Context, identity *ProjectIdentity) (*ProjectIdentity, error) {
logging.Debugf(c, "lookup project scoped identity %v", identity)
tmp := *identity
if err := ds.Get(c, &tmp); err != nil {
switch {
case err == ds.ErrNoSuchEntity:
return nil, ErrNotFound
case err != nil:
return nil, transient.Tag.Apply(err)
}
}
return &tmp, nil
}
// LookupByProject returns the project identity stored for a given project.
func (s *persistentStorage) LookupByProject(c context.Context, project string) (*ProjectIdentity, error) {
return s.lookup(c, &ProjectIdentity{Project: project})
}
// Delete removes an identity from the storage.
func (s *persistentStorage) Delete(c context.Context, identity *ProjectIdentity) error {
logging.Debugf(c, "delete project scoped identity %v", identity)
return ds.Delete(c, identity)
}
// Create stores a new entry for a project identity.
func (s *persistentStorage) Create(c context.Context, identity *ProjectIdentity) (*ProjectIdentity, error) {
logging.Debugf(c, "create project scoped identity %v", identity)
return s.Update(c, identity)
}
// Update allows an identity to be updated, e.g. when the service account email changes.
func (s *persistentStorage) Update(c context.Context, identity *ProjectIdentity) (*ProjectIdentity, error) {
logging.Debugf(c, "update project scoped identity %v", identity)
tmp, err := s.lookup(c, identity)
switch {
case err == nil && *tmp == *identity: // Doesn't need update
return identity, nil
case err != nil && err != ErrNotFound: // Lookup error to propagate
return nil, err
}
if err := ds.Put(c, identity); err != nil {
return nil, err
}
return identity, nil
}