Merge pull request #5019 from taylorsilva/wall-3636
Add API endpoints for broadcasting a message to a concourse cluster
This commit is contained in:
commit
602b60c7fd
|
@ -6,7 +6,6 @@ import (
|
|||
"net/http/httptest"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"code.cloudfoundry.org/lager"
|
||||
"code.cloudfoundry.org/lager/lagertest"
|
||||
|
@ -52,12 +51,12 @@ var (
|
|||
dbUserFactory *dbfakes.FakeUserFactory
|
||||
dbCheckFactory *dbfakes.FakeCheckFactory
|
||||
dbTeam *dbfakes.FakeTeam
|
||||
dbWall *dbfakes.FakeWall
|
||||
fakeSecretManager *credsfakes.FakeSecrets
|
||||
fakeVarSourcePool *credsfakes.FakeVarSourcePool
|
||||
credsManagers creds.Managers
|
||||
interceptTimeoutFactory *containerserverfakes.FakeInterceptTimeoutFactory
|
||||
interceptTimeout *containerserverfakes.FakeInterceptTimeout
|
||||
expire time.Duration
|
||||
isTLSEnabled bool
|
||||
cliDownloadsDir string
|
||||
logger *lagertest.TestLogger
|
||||
|
@ -97,6 +96,7 @@ var _ = BeforeEach(func() {
|
|||
dbBuildFactory = new(dbfakes.FakeBuildFactory)
|
||||
dbUserFactory = new(dbfakes.FakeUserFactory)
|
||||
dbCheckFactory = new(dbfakes.FakeCheckFactory)
|
||||
dbWall = new(dbfakes.FakeWall)
|
||||
|
||||
interceptTimeoutFactory = new(containerserverfakes.FakeInterceptTimeoutFactory)
|
||||
interceptTimeout = new(containerserverfakes.FakeInterceptTimeout)
|
||||
|
@ -137,8 +137,6 @@ var _ = BeforeEach(func() {
|
|||
|
||||
sink = lager.NewReconfigurableSink(lager.NewPrettySink(GinkgoWriter, lager.DEBUG), lager.DEBUG)
|
||||
|
||||
expire = 24 * time.Hour
|
||||
|
||||
isTLSEnabled = false
|
||||
|
||||
build = new(dbfakes.FakeBuild)
|
||||
|
@ -192,6 +190,7 @@ var _ = BeforeEach(func() {
|
|||
fakeVarSourcePool,
|
||||
credsManagers,
|
||||
interceptTimeoutFactory,
|
||||
dbWall,
|
||||
)
|
||||
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
|
|
@ -4,8 +4,6 @@ import (
|
|||
"net/http"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/concourse/concourse/atc/api/usersserver"
|
||||
|
||||
"code.cloudfoundry.org/lager"
|
||||
"github.com/concourse/concourse/atc"
|
||||
"github.com/concourse/concourse/atc/api/artifactserver"
|
||||
|
@ -22,7 +20,9 @@ import (
|
|||
"github.com/concourse/concourse/atc/api/resourceserver"
|
||||
"github.com/concourse/concourse/atc/api/resourceserver/versionserver"
|
||||
"github.com/concourse/concourse/atc/api/teamserver"
|
||||
"github.com/concourse/concourse/atc/api/usersserver"
|
||||
"github.com/concourse/concourse/atc/api/volumeserver"
|
||||
"github.com/concourse/concourse/atc/api/wallserver"
|
||||
"github.com/concourse/concourse/atc/api/workerserver"
|
||||
"github.com/concourse/concourse/atc/creds"
|
||||
"github.com/concourse/concourse/atc/db"
|
||||
|
@ -69,6 +69,7 @@ func NewHandler(
|
|||
varSourcePool creds.VarSourcePool,
|
||||
credsManagers creds.Managers,
|
||||
interceptTimeoutFactory containerserver.InterceptTimeoutFactory,
|
||||
dbWall db.Wall,
|
||||
) (http.Handler, error) {
|
||||
|
||||
absCLIDownloadsDir, err := filepath.Abs(cliDownloadsDir)
|
||||
|
@ -98,6 +99,7 @@ func NewHandler(
|
|||
infoServer := infoserver.NewServer(logger, version, workerVersion, externalURL, clusterName, credsManagers)
|
||||
artifactServer := artifactserver.NewServer(logger, workerClient)
|
||||
usersServer := usersserver.NewServer(logger, dbUserFactory)
|
||||
wallServer := wallserver.NewServer(dbWall, logger)
|
||||
|
||||
handlers := map[string]http.Handler{
|
||||
atc.GetConfig: http.HandlerFunc(configServer.GetConfig),
|
||||
|
@ -204,6 +206,10 @@ func NewHandler(
|
|||
|
||||
atc.CreateArtifact: teamHandlerFactory.HandlerFor(artifactServer.CreateArtifact),
|
||||
atc.GetArtifact: teamHandlerFactory.HandlerFor(artifactServer.GetArtifact),
|
||||
|
||||
atc.GetWall: http.HandlerFunc(wallServer.GetWall),
|
||||
atc.SetWall: http.HandlerFunc(wallServer.SetWall),
|
||||
atc.ClearWall: http.HandlerFunc(wallServer.ClearWall),
|
||||
}
|
||||
|
||||
return rata.NewRouter(atc.Routes, wrapper.Wrap(handlers))
|
||||
|
|
|
@ -14,7 +14,8 @@ func (s *Server) Info(w http.ResponseWriter, r *http.Request) {
|
|||
err := json.NewEncoder(w).Encode(atc.Info{Version: s.version,
|
||||
WorkerVersion: s.workerVersion,
|
||||
ExternalURL: s.externalURL,
|
||||
ClusterName: s.clusterName})
|
||||
ClusterName: s.clusterName,
|
||||
})
|
||||
if err != nil {
|
||||
logger.Error("failed-to-encode-info", err)
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
|
|
|
@ -0,0 +1,185 @@
|
|||
package api_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/concourse/concourse/atc"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
var _ = Describe("Wall API", func() {
|
||||
var response *http.Response
|
||||
Context("Gets a wall message", func() {
|
||||
BeforeEach(func() {
|
||||
dbWall.GetWallReturns(atc.Wall{Message: "test message"}, nil)
|
||||
})
|
||||
|
||||
JustBeforeEach(func() {
|
||||
req, err := http.NewRequest("GET", server.URL+"/api/v1/wall", nil)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
response, err = client.Do(req)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
|
||||
It("returns 200", func() {
|
||||
Expect(response.StatusCode).To(Equal(http.StatusOK))
|
||||
})
|
||||
|
||||
It("returns Content-Type 'application/json'", func() {
|
||||
Expect(response.Header.Get("Content-Type")).To(Equal("application/json"))
|
||||
})
|
||||
|
||||
Context("the message does not expire", func() {
|
||||
|
||||
It("returns only message", func() {
|
||||
Expect(dbWall.GetWallCallCount()).To(Equal(1))
|
||||
Expect(ioutil.ReadAll(response.Body)).To(MatchJSON(`{"message":"test message"}`))
|
||||
})
|
||||
})
|
||||
|
||||
Context("and the message does expire", func() {
|
||||
var (
|
||||
expectedDuration time.Duration
|
||||
)
|
||||
BeforeEach(func() {
|
||||
expiresAt := time.Now().Add(time.Minute)
|
||||
expectedDuration = time.Until(expiresAt)
|
||||
dbWall.GetWallReturns(atc.Wall{Message: "test message", TTL: expectedDuration}, nil)
|
||||
})
|
||||
|
||||
It("returns the expiration with the message", func() {
|
||||
Expect(dbWall.GetWallCallCount()).To(Equal(1))
|
||||
|
||||
var msg atc.Wall
|
||||
err := json.NewDecoder(response.Body).Decode(&msg)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(msg).To(Equal(atc.Wall{
|
||||
Message: "test message",
|
||||
TTL: expectedDuration,
|
||||
}))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Context("Sets a wall message", func() {
|
||||
var expectedWall atc.Wall
|
||||
BeforeEach(func() {
|
||||
expectedWall = atc.Wall{
|
||||
Message: "test message",
|
||||
TTL: time.Minute,
|
||||
}
|
||||
|
||||
dbWall.SetWallReturns(nil)
|
||||
})
|
||||
|
||||
JustBeforeEach(func() {
|
||||
payload, err := json.Marshal(expectedWall)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
req, err := http.NewRequest("PUT", server.URL+"/api/v1/wall",
|
||||
ioutil.NopCloser(bytes.NewBuffer(payload)))
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
response, err = client.Do(req)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
|
||||
Context("when authenticated", func() {
|
||||
BeforeEach(func() {
|
||||
fakeAccess.IsAuthenticatedReturns(true)
|
||||
})
|
||||
|
||||
Context("and is admin", func() {
|
||||
BeforeEach(func() {
|
||||
fakeAccess.IsAdminReturns(true)
|
||||
})
|
||||
|
||||
It("returns 200", func() {
|
||||
Expect(response.StatusCode).To(Equal(http.StatusOK))
|
||||
})
|
||||
|
||||
It("sets the message and expiration", func() {
|
||||
Expect(dbWall.SetWallCallCount()).To(Equal(1))
|
||||
Expect(dbWall.SetWallArgsForCall(0)).To(Equal(expectedWall))
|
||||
})
|
||||
})
|
||||
|
||||
Context("and is not admin", func() {
|
||||
BeforeEach(func() {
|
||||
fakeAccess.IsAdminReturns(false)
|
||||
})
|
||||
|
||||
It("returns 403", func() {
|
||||
Expect(response.StatusCode).To(Equal(http.StatusForbidden))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Context("when not authenticated", func() {
|
||||
BeforeEach(func() {
|
||||
fakeAccess.IsAuthenticatedReturns(false)
|
||||
})
|
||||
|
||||
It("returns 401", func() {
|
||||
Expect(response.StatusCode).To(Equal(http.StatusUnauthorized))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Context("Clears the wall message", func() {
|
||||
JustBeforeEach(func() {
|
||||
req, err := http.NewRequest("DELETE", server.URL+"/api/v1/wall", nil)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
response, err = client.Do(req)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
|
||||
Context("when authenticated", func() {
|
||||
BeforeEach(func() {
|
||||
fakeAccess.IsAuthenticatedReturns(true)
|
||||
})
|
||||
|
||||
Context("is an admin", func() {
|
||||
BeforeEach(func() {
|
||||
fakeAccess.IsAdminReturns(true)
|
||||
})
|
||||
|
||||
It("returns 200", func() {
|
||||
Expect(response.StatusCode).To(Equal(http.StatusOK))
|
||||
})
|
||||
|
||||
It("makes the Clear database call", func() {
|
||||
Expect(dbWall.ClearCallCount()).To(Equal(1))
|
||||
})
|
||||
})
|
||||
Context("is not an admin", func() {
|
||||
BeforeEach(func() {
|
||||
fakeAccess.IsAdminReturns(false)
|
||||
})
|
||||
|
||||
It("returns 403", func() {
|
||||
Expect(response.StatusCode).To(Equal(http.StatusForbidden))
|
||||
})
|
||||
})
|
||||
})
|
||||
Context("when not authenticated", func() {
|
||||
BeforeEach(func() {
|
||||
fakeAccess.IsAuthenticatedReturns(false)
|
||||
})
|
||||
|
||||
It("returns 401", func() {
|
||||
Expect(response.StatusCode).To(Equal(http.StatusUnauthorized))
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
})
|
|
@ -0,0 +1,18 @@
|
|||
package wallserver
|
||||
|
||||
import (
|
||||
"code.cloudfoundry.org/lager"
|
||||
"github.com/concourse/concourse/atc/db"
|
||||
)
|
||||
|
||||
type Server struct {
|
||||
wall db.Wall
|
||||
logger lager.Logger
|
||||
}
|
||||
|
||||
func NewServer(wall db.Wall, logger lager.Logger) *Server {
|
||||
return &Server{
|
||||
wall: wall,
|
||||
logger: logger,
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
package wallserver
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/concourse/concourse/atc"
|
||||
)
|
||||
|
||||
func (s *Server) GetWall(w http.ResponseWriter, r *http.Request) {
|
||||
logger := s.logger.Session("wall")
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
|
||||
wall, err := s.wall.GetWall()
|
||||
if err != nil {
|
||||
logger.Error("failed-to-get-message", err)
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
err = json.NewEncoder(w).Encode(wall)
|
||||
if err != nil {
|
||||
logger.Error("failed-to-encode-json", err)
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) SetWall(w http.ResponseWriter, r *http.Request) {
|
||||
logger := s.logger.Session("wall")
|
||||
|
||||
var wall atc.Wall
|
||||
err := json.NewDecoder(r.Body).Decode(&wall)
|
||||
if err != nil {
|
||||
logger.Error("failed-to-decode-json", err)
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
err = s.wall.SetWall(wall)
|
||||
if err != nil {
|
||||
logger.Error("failed-to-set-wall-message", err)
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) ClearWall(w http.ResponseWriter, r *http.Request) {
|
||||
logger := s.logger.Session("wall")
|
||||
err := s.wall.Clear()
|
||||
if err != nil {
|
||||
logger.Error("failed-to-clear-the-wall", err)
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
}
|
||||
}
|
|
@ -615,6 +615,8 @@ func (cmd *RunCommand) constructAPIMembers(
|
|||
gcContainerDestroyer := gc.NewDestroyer(logger, dbContainerRepository, dbVolumeRepository)
|
||||
dbBuildFactory := db.NewBuildFactory(dbConn, lockFactory, cmd.GC.OneOffBuildGracePeriod)
|
||||
dbCheckFactory := db.NewCheckFactory(dbConn, lockFactory, secretManager, cmd.varSourcePool, cmd.GlobalResourceCheckTimeout)
|
||||
dbClock := db.NewClock()
|
||||
dbWall := db.NewWall(dbConn, &dbClock)
|
||||
|
||||
accessFactory := accessor.NewAccessFactory(authHandler.PublicKey())
|
||||
customActionRoleMap := accessor.CustomActionRoleMap{}
|
||||
|
@ -646,6 +648,7 @@ func (cmd *RunCommand) constructAPIMembers(
|
|||
secretManager,
|
||||
credsManagers,
|
||||
accessFactory,
|
||||
dbWall,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
|
@ -1537,6 +1540,7 @@ func (cmd *RunCommand) constructAPIHandler(
|
|||
secretManager creds.Secrets,
|
||||
credsManagers creds.Managers,
|
||||
accessFactory accessor.AccessFactory,
|
||||
dbWall db.Wall,
|
||||
) (http.Handler, error) {
|
||||
|
||||
checkPipelineAccessHandlerFactory := auth.NewCheckPipelineAccessHandlerFactory(teamFactory)
|
||||
|
@ -1603,6 +1607,7 @@ func (cmd *RunCommand) constructAPIHandler(
|
|||
cmd.varSourcePool,
|
||||
credsManagers,
|
||||
containerserver.NewInterceptTimeoutFactory(cmd.InterceptIdleTimeout),
|
||||
dbWall,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -129,7 +129,10 @@ func (a *auditor) ValidateAction(action string) bool {
|
|||
atc.DownloadCLI,
|
||||
atc.GetInfo,
|
||||
atc.GetInfoCreds,
|
||||
atc.ListActiveUsersSince:
|
||||
atc.ListActiveUsersSince,
|
||||
atc.GetWall,
|
||||
atc.SetWall,
|
||||
atc.ClearWall:
|
||||
return a.EnableSystemAuditLog
|
||||
case atc.ListTeams,
|
||||
atc.SetTeam,
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
package db
|
||||
|
||||
import "time"
|
||||
|
||||
//go:generate counterfeiter . Clock
|
||||
|
||||
type Clock interface {
|
||||
Now() time.Time
|
||||
Until(time.Time) time.Duration
|
||||
}
|
||||
|
||||
type clock struct{}
|
||||
|
||||
func NewClock() clock {
|
||||
return clock{}
|
||||
}
|
||||
|
||||
func (c *clock) Now() time.Time {
|
||||
return time.Now()
|
||||
}
|
||||
|
||||
func (c *clock) Until(t time.Time) time.Duration {
|
||||
return time.Until(t)
|
||||
}
|
|
@ -14,6 +14,7 @@ import (
|
|||
"github.com/concourse/concourse/atc"
|
||||
"github.com/concourse/concourse/atc/creds/credsfakes"
|
||||
"github.com/concourse/concourse/atc/db"
|
||||
"github.com/concourse/concourse/atc/db/dbfakes"
|
||||
"github.com/concourse/concourse/atc/db/lock"
|
||||
"github.com/concourse/concourse/atc/metric"
|
||||
"github.com/concourse/concourse/atc/postgresrunner"
|
||||
|
@ -47,6 +48,8 @@ var (
|
|||
workerBaseResourceTypeFactory db.WorkerBaseResourceTypeFactory
|
||||
workerTaskCacheFactory db.WorkerTaskCacheFactory
|
||||
userFactory db.UserFactory
|
||||
dbWall db.Wall
|
||||
fakeClock dbfakes.FakeClock
|
||||
|
||||
defaultWorkerResourceType atc.WorkerResourceType
|
||||
defaultTeam db.Team
|
||||
|
@ -116,6 +119,7 @@ var _ = BeforeEach(func() {
|
|||
workerBaseResourceTypeFactory = db.NewWorkerBaseResourceTypeFactory(dbConn)
|
||||
workerTaskCacheFactory = db.NewWorkerTaskCacheFactory(dbConn)
|
||||
userFactory = db.NewUserFactory(dbConn)
|
||||
dbWall = db.NewWall(dbConn, &fakeClock)
|
||||
|
||||
var err error
|
||||
defaultTeam, err = teamFactory.CreateTeam(atc.Team{Name: "default-team"})
|
||||
|
|
|
@ -0,0 +1,175 @@
|
|||
// Code generated by counterfeiter. DO NOT EDIT.
|
||||
package dbfakes
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/concourse/concourse/atc/db"
|
||||
)
|
||||
|
||||
type FakeClock struct {
|
||||
NowStub func() time.Time
|
||||
nowMutex sync.RWMutex
|
||||
nowArgsForCall []struct {
|
||||
}
|
||||
nowReturns struct {
|
||||
result1 time.Time
|
||||
}
|
||||
nowReturnsOnCall map[int]struct {
|
||||
result1 time.Time
|
||||
}
|
||||
UntilStub func(time.Time) time.Duration
|
||||
untilMutex sync.RWMutex
|
||||
untilArgsForCall []struct {
|
||||
arg1 time.Time
|
||||
}
|
||||
untilReturns struct {
|
||||
result1 time.Duration
|
||||
}
|
||||
untilReturnsOnCall map[int]struct {
|
||||
result1 time.Duration
|
||||
}
|
||||
invocations map[string][][]interface{}
|
||||
invocationsMutex sync.RWMutex
|
||||
}
|
||||
|
||||
func (fake *FakeClock) Now() time.Time {
|
||||
fake.nowMutex.Lock()
|
||||
ret, specificReturn := fake.nowReturnsOnCall[len(fake.nowArgsForCall)]
|
||||
fake.nowArgsForCall = append(fake.nowArgsForCall, struct {
|
||||
}{})
|
||||
fake.recordInvocation("Now", []interface{}{})
|
||||
fake.nowMutex.Unlock()
|
||||
if fake.NowStub != nil {
|
||||
return fake.NowStub()
|
||||
}
|
||||
if specificReturn {
|
||||
return ret.result1
|
||||
}
|
||||
fakeReturns := fake.nowReturns
|
||||
return fakeReturns.result1
|
||||
}
|
||||
|
||||
func (fake *FakeClock) NowCallCount() int {
|
||||
fake.nowMutex.RLock()
|
||||
defer fake.nowMutex.RUnlock()
|
||||
return len(fake.nowArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeClock) NowCalls(stub func() time.Time) {
|
||||
fake.nowMutex.Lock()
|
||||
defer fake.nowMutex.Unlock()
|
||||
fake.NowStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeClock) NowReturns(result1 time.Time) {
|
||||
fake.nowMutex.Lock()
|
||||
defer fake.nowMutex.Unlock()
|
||||
fake.NowStub = nil
|
||||
fake.nowReturns = struct {
|
||||
result1 time.Time
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeClock) NowReturnsOnCall(i int, result1 time.Time) {
|
||||
fake.nowMutex.Lock()
|
||||
defer fake.nowMutex.Unlock()
|
||||
fake.NowStub = nil
|
||||
if fake.nowReturnsOnCall == nil {
|
||||
fake.nowReturnsOnCall = make(map[int]struct {
|
||||
result1 time.Time
|
||||
})
|
||||
}
|
||||
fake.nowReturnsOnCall[i] = struct {
|
||||
result1 time.Time
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeClock) Until(arg1 time.Time) time.Duration {
|
||||
fake.untilMutex.Lock()
|
||||
ret, specificReturn := fake.untilReturnsOnCall[len(fake.untilArgsForCall)]
|
||||
fake.untilArgsForCall = append(fake.untilArgsForCall, struct {
|
||||
arg1 time.Time
|
||||
}{arg1})
|
||||
fake.recordInvocation("Until", []interface{}{arg1})
|
||||
fake.untilMutex.Unlock()
|
||||
if fake.UntilStub != nil {
|
||||
return fake.UntilStub(arg1)
|
||||
}
|
||||
if specificReturn {
|
||||
return ret.result1
|
||||
}
|
||||
fakeReturns := fake.untilReturns
|
||||
return fakeReturns.result1
|
||||
}
|
||||
|
||||
func (fake *FakeClock) UntilCallCount() int {
|
||||
fake.untilMutex.RLock()
|
||||
defer fake.untilMutex.RUnlock()
|
||||
return len(fake.untilArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeClock) UntilCalls(stub func(time.Time) time.Duration) {
|
||||
fake.untilMutex.Lock()
|
||||
defer fake.untilMutex.Unlock()
|
||||
fake.UntilStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeClock) UntilArgsForCall(i int) time.Time {
|
||||
fake.untilMutex.RLock()
|
||||
defer fake.untilMutex.RUnlock()
|
||||
argsForCall := fake.untilArgsForCall[i]
|
||||
return argsForCall.arg1
|
||||
}
|
||||
|
||||
func (fake *FakeClock) UntilReturns(result1 time.Duration) {
|
||||
fake.untilMutex.Lock()
|
||||
defer fake.untilMutex.Unlock()
|
||||
fake.UntilStub = nil
|
||||
fake.untilReturns = struct {
|
||||
result1 time.Duration
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeClock) UntilReturnsOnCall(i int, result1 time.Duration) {
|
||||
fake.untilMutex.Lock()
|
||||
defer fake.untilMutex.Unlock()
|
||||
fake.UntilStub = nil
|
||||
if fake.untilReturnsOnCall == nil {
|
||||
fake.untilReturnsOnCall = make(map[int]struct {
|
||||
result1 time.Duration
|
||||
})
|
||||
}
|
||||
fake.untilReturnsOnCall[i] = struct {
|
||||
result1 time.Duration
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeClock) Invocations() map[string][][]interface{} {
|
||||
fake.invocationsMutex.RLock()
|
||||
defer fake.invocationsMutex.RUnlock()
|
||||
fake.nowMutex.RLock()
|
||||
defer fake.nowMutex.RUnlock()
|
||||
fake.untilMutex.RLock()
|
||||
defer fake.untilMutex.RUnlock()
|
||||
copiedInvocations := map[string][][]interface{}{}
|
||||
for key, value := range fake.invocations {
|
||||
copiedInvocations[key] = value
|
||||
}
|
||||
return copiedInvocations
|
||||
}
|
||||
|
||||
func (fake *FakeClock) recordInvocation(key string, args []interface{}) {
|
||||
fake.invocationsMutex.Lock()
|
||||
defer fake.invocationsMutex.Unlock()
|
||||
if fake.invocations == nil {
|
||||
fake.invocations = map[string][][]interface{}{}
|
||||
}
|
||||
if fake.invocations[key] == nil {
|
||||
fake.invocations[key] = [][]interface{}{}
|
||||
}
|
||||
fake.invocations[key] = append(fake.invocations[key], args)
|
||||
}
|
||||
|
||||
var _ db.Clock = new(FakeClock)
|
|
@ -0,0 +1,244 @@
|
|||
// Code generated by counterfeiter. DO NOT EDIT.
|
||||
package dbfakes
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/concourse/concourse/atc"
|
||||
"github.com/concourse/concourse/atc/db"
|
||||
)
|
||||
|
||||
type FakeWall struct {
|
||||
ClearStub func() error
|
||||
clearMutex sync.RWMutex
|
||||
clearArgsForCall []struct {
|
||||
}
|
||||
clearReturns struct {
|
||||
result1 error
|
||||
}
|
||||
clearReturnsOnCall map[int]struct {
|
||||
result1 error
|
||||
}
|
||||
GetWallStub func() (atc.Wall, error)
|
||||
getWallMutex sync.RWMutex
|
||||
getWallArgsForCall []struct {
|
||||
}
|
||||
getWallReturns struct {
|
||||
result1 atc.Wall
|
||||
result2 error
|
||||
}
|
||||
getWallReturnsOnCall map[int]struct {
|
||||
result1 atc.Wall
|
||||
result2 error
|
||||
}
|
||||
SetWallStub func(atc.Wall) error
|
||||
setWallMutex sync.RWMutex
|
||||
setWallArgsForCall []struct {
|
||||
arg1 atc.Wall
|
||||
}
|
||||
setWallReturns struct {
|
||||
result1 error
|
||||
}
|
||||
setWallReturnsOnCall map[int]struct {
|
||||
result1 error
|
||||
}
|
||||
invocations map[string][][]interface{}
|
||||
invocationsMutex sync.RWMutex
|
||||
}
|
||||
|
||||
func (fake *FakeWall) Clear() error {
|
||||
fake.clearMutex.Lock()
|
||||
ret, specificReturn := fake.clearReturnsOnCall[len(fake.clearArgsForCall)]
|
||||
fake.clearArgsForCall = append(fake.clearArgsForCall, struct {
|
||||
}{})
|
||||
fake.recordInvocation("Clear", []interface{}{})
|
||||
fake.clearMutex.Unlock()
|
||||
if fake.ClearStub != nil {
|
||||
return fake.ClearStub()
|
||||
}
|
||||
if specificReturn {
|
||||
return ret.result1
|
||||
}
|
||||
fakeReturns := fake.clearReturns
|
||||
return fakeReturns.result1
|
||||
}
|
||||
|
||||
func (fake *FakeWall) ClearCallCount() int {
|
||||
fake.clearMutex.RLock()
|
||||
defer fake.clearMutex.RUnlock()
|
||||
return len(fake.clearArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeWall) ClearCalls(stub func() error) {
|
||||
fake.clearMutex.Lock()
|
||||
defer fake.clearMutex.Unlock()
|
||||
fake.ClearStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeWall) ClearReturns(result1 error) {
|
||||
fake.clearMutex.Lock()
|
||||
defer fake.clearMutex.Unlock()
|
||||
fake.ClearStub = nil
|
||||
fake.clearReturns = struct {
|
||||
result1 error
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeWall) ClearReturnsOnCall(i int, result1 error) {
|
||||
fake.clearMutex.Lock()
|
||||
defer fake.clearMutex.Unlock()
|
||||
fake.ClearStub = nil
|
||||
if fake.clearReturnsOnCall == nil {
|
||||
fake.clearReturnsOnCall = make(map[int]struct {
|
||||
result1 error
|
||||
})
|
||||
}
|
||||
fake.clearReturnsOnCall[i] = struct {
|
||||
result1 error
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeWall) GetWall() (atc.Wall, error) {
|
||||
fake.getWallMutex.Lock()
|
||||
ret, specificReturn := fake.getWallReturnsOnCall[len(fake.getWallArgsForCall)]
|
||||
fake.getWallArgsForCall = append(fake.getWallArgsForCall, struct {
|
||||
}{})
|
||||
fake.recordInvocation("GetWall", []interface{}{})
|
||||
fake.getWallMutex.Unlock()
|
||||
if fake.GetWallStub != nil {
|
||||
return fake.GetWallStub()
|
||||
}
|
||||
if specificReturn {
|
||||
return ret.result1, ret.result2
|
||||
}
|
||||
fakeReturns := fake.getWallReturns
|
||||
return fakeReturns.result1, fakeReturns.result2
|
||||
}
|
||||
|
||||
func (fake *FakeWall) GetWallCallCount() int {
|
||||
fake.getWallMutex.RLock()
|
||||
defer fake.getWallMutex.RUnlock()
|
||||
return len(fake.getWallArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeWall) GetWallCalls(stub func() (atc.Wall, error)) {
|
||||
fake.getWallMutex.Lock()
|
||||
defer fake.getWallMutex.Unlock()
|
||||
fake.GetWallStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeWall) GetWallReturns(result1 atc.Wall, result2 error) {
|
||||
fake.getWallMutex.Lock()
|
||||
defer fake.getWallMutex.Unlock()
|
||||
fake.GetWallStub = nil
|
||||
fake.getWallReturns = struct {
|
||||
result1 atc.Wall
|
||||
result2 error
|
||||
}{result1, result2}
|
||||
}
|
||||
|
||||
func (fake *FakeWall) GetWallReturnsOnCall(i int, result1 atc.Wall, result2 error) {
|
||||
fake.getWallMutex.Lock()
|
||||
defer fake.getWallMutex.Unlock()
|
||||
fake.GetWallStub = nil
|
||||
if fake.getWallReturnsOnCall == nil {
|
||||
fake.getWallReturnsOnCall = make(map[int]struct {
|
||||
result1 atc.Wall
|
||||
result2 error
|
||||
})
|
||||
}
|
||||
fake.getWallReturnsOnCall[i] = struct {
|
||||
result1 atc.Wall
|
||||
result2 error
|
||||
}{result1, result2}
|
||||
}
|
||||
|
||||
func (fake *FakeWall) SetWall(arg1 atc.Wall) error {
|
||||
fake.setWallMutex.Lock()
|
||||
ret, specificReturn := fake.setWallReturnsOnCall[len(fake.setWallArgsForCall)]
|
||||
fake.setWallArgsForCall = append(fake.setWallArgsForCall, struct {
|
||||
arg1 atc.Wall
|
||||
}{arg1})
|
||||
fake.recordInvocation("SetWall", []interface{}{arg1})
|
||||
fake.setWallMutex.Unlock()
|
||||
if fake.SetWallStub != nil {
|
||||
return fake.SetWallStub(arg1)
|
||||
}
|
||||
if specificReturn {
|
||||
return ret.result1
|
||||
}
|
||||
fakeReturns := fake.setWallReturns
|
||||
return fakeReturns.result1
|
||||
}
|
||||
|
||||
func (fake *FakeWall) SetWallCallCount() int {
|
||||
fake.setWallMutex.RLock()
|
||||
defer fake.setWallMutex.RUnlock()
|
||||
return len(fake.setWallArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeWall) SetWallCalls(stub func(atc.Wall) error) {
|
||||
fake.setWallMutex.Lock()
|
||||
defer fake.setWallMutex.Unlock()
|
||||
fake.SetWallStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeWall) SetWallArgsForCall(i int) atc.Wall {
|
||||
fake.setWallMutex.RLock()
|
||||
defer fake.setWallMutex.RUnlock()
|
||||
argsForCall := fake.setWallArgsForCall[i]
|
||||
return argsForCall.arg1
|
||||
}
|
||||
|
||||
func (fake *FakeWall) SetWallReturns(result1 error) {
|
||||
fake.setWallMutex.Lock()
|
||||
defer fake.setWallMutex.Unlock()
|
||||
fake.SetWallStub = nil
|
||||
fake.setWallReturns = struct {
|
||||
result1 error
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeWall) SetWallReturnsOnCall(i int, result1 error) {
|
||||
fake.setWallMutex.Lock()
|
||||
defer fake.setWallMutex.Unlock()
|
||||
fake.SetWallStub = nil
|
||||
if fake.setWallReturnsOnCall == nil {
|
||||
fake.setWallReturnsOnCall = make(map[int]struct {
|
||||
result1 error
|
||||
})
|
||||
}
|
||||
fake.setWallReturnsOnCall[i] = struct {
|
||||
result1 error
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeWall) Invocations() map[string][][]interface{} {
|
||||
fake.invocationsMutex.RLock()
|
||||
defer fake.invocationsMutex.RUnlock()
|
||||
fake.clearMutex.RLock()
|
||||
defer fake.clearMutex.RUnlock()
|
||||
fake.getWallMutex.RLock()
|
||||
defer fake.getWallMutex.RUnlock()
|
||||
fake.setWallMutex.RLock()
|
||||
defer fake.setWallMutex.RUnlock()
|
||||
copiedInvocations := map[string][][]interface{}{}
|
||||
for key, value := range fake.invocations {
|
||||
copiedInvocations[key] = value
|
||||
}
|
||||
return copiedInvocations
|
||||
}
|
||||
|
||||
func (fake *FakeWall) recordInvocation(key string, args []interface{}) {
|
||||
fake.invocationsMutex.Lock()
|
||||
defer fake.invocationsMutex.Unlock()
|
||||
if fake.invocations == nil {
|
||||
fake.invocations = map[string][][]interface{}{}
|
||||
}
|
||||
if fake.invocations[key] == nil {
|
||||
fake.invocations[key] = [][]interface{}{}
|
||||
}
|
||||
fake.invocations[key] = append(fake.invocations[key], args)
|
||||
}
|
||||
|
||||
var _ db.Wall = new(FakeWall)
|
|
@ -0,0 +1,5 @@
|
|||
BEGIN;
|
||||
|
||||
DROP TABLE IF EXISTS wall;
|
||||
|
||||
COMMIT;
|
|
@ -0,0 +1,8 @@
|
|||
BEGIN;
|
||||
|
||||
CREATE TABLE wall (
|
||||
message text NOT NULL,
|
||||
expires_at timestamp with time zone
|
||||
);
|
||||
|
||||
COMMIT;
|
|
@ -0,0 +1,107 @@
|
|||
package db
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
|
||||
"github.com/concourse/concourse/atc"
|
||||
|
||||
sq "github.com/Masterminds/squirrel"
|
||||
)
|
||||
|
||||
//go:generate counterfeiter . Wall
|
||||
|
||||
type Wall interface {
|
||||
SetWall(atc.Wall) error
|
||||
GetWall() (atc.Wall, error)
|
||||
Clear() error
|
||||
}
|
||||
|
||||
type wall struct {
|
||||
conn Conn
|
||||
clock Clock
|
||||
}
|
||||
|
||||
func NewWall(conn Conn, clock Clock) Wall {
|
||||
return &wall{
|
||||
conn: conn,
|
||||
clock: clock,
|
||||
}
|
||||
}
|
||||
|
||||
func (w wall) SetWall(wall atc.Wall) error {
|
||||
tx, err := w.conn.Begin()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer Rollback(tx)
|
||||
|
||||
_, err = psql.Delete("wall").RunWith(tx).Exec()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
query := psql.Insert("wall").
|
||||
Columns("message")
|
||||
|
||||
if wall.TTL != 0 {
|
||||
expiresAt := w.clock.Now().Add(wall.TTL)
|
||||
query = query.Columns("expires_at").Values(wall.Message, expiresAt)
|
||||
} else {
|
||||
query = query.Values(wall.Message)
|
||||
}
|
||||
|
||||
_, err = query.RunWith(tx).Exec()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = tx.Commit()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w wall) GetWall() (atc.Wall, error) {
|
||||
var wall atc.Wall
|
||||
|
||||
row := psql.Select("message", "expires_at").
|
||||
From("wall").
|
||||
Where(sq.Or{
|
||||
sq.Gt{"expires_at": w.clock.Now()},
|
||||
sq.Eq{"expires_at": nil},
|
||||
}).
|
||||
RunWith(w.conn).QueryRow()
|
||||
|
||||
err := w.scanWall(&wall, row)
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
return atc.Wall{}, err
|
||||
}
|
||||
|
||||
return wall, nil
|
||||
}
|
||||
|
||||
func (w *wall) scanWall(wall *atc.Wall, scan scannable) error {
|
||||
var expiresAt sql.NullTime
|
||||
|
||||
err := scan.Scan(&wall.Message, &expiresAt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if expiresAt.Valid {
|
||||
wall.TTL = w.clock.Until(expiresAt.Time)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w wall) Clear() error {
|
||||
_, err := psql.Delete("wall").RunWith(w.conn).Exec()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,119 @@
|
|||
package db_test
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/concourse/concourse/atc"
|
||||
"github.com/concourse/concourse/atc/db/dbfakes"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
var _ = Describe("Wall", func() {
|
||||
var (
|
||||
msgOnly = atc.Wall{Message: "this is a test message!"}
|
||||
msgWithTTL = atc.Wall{Message: "this is a test message!", TTL: time.Minute}
|
||||
startTime = time.Now()
|
||||
)
|
||||
|
||||
Context(" a message is set", func() {
|
||||
BeforeEach(func() {
|
||||
fakeClock = dbfakes.FakeClock{}
|
||||
fakeClock.NowReturns(startTime)
|
||||
})
|
||||
Context("with no expiration", func() {
|
||||
It("successfully sets the wall", func() {
|
||||
err := dbWall.SetWall(msgOnly)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(fakeClock.NowCallCount()).To(Equal(0))
|
||||
})
|
||||
|
||||
It("successfully gets the wall", func() {
|
||||
_ = dbWall.SetWall(msgOnly)
|
||||
actualWall, err := dbWall.GetWall()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(fakeClock.NowCallCount()).To(Equal(1))
|
||||
Expect(actualWall).To(Equal(msgOnly))
|
||||
})
|
||||
|
||||
})
|
||||
Context("with an expiration", func() {
|
||||
It("successfully sets the wall", func() {
|
||||
err := dbWall.SetWall(msgWithTTL)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(fakeClock.NowCallCount()).To(Equal(1))
|
||||
})
|
||||
|
||||
Context("the message has not expired", func() {
|
||||
Context("and gets a wall", func() {
|
||||
BeforeEach(func() {
|
||||
fakeClock.NowReturns(startTime.Add(time.Second))
|
||||
fakeClock.UntilReturns(30 * time.Second)
|
||||
})
|
||||
|
||||
Specify("successfully", func() {
|
||||
_ = dbWall.SetWall(msgWithTTL)
|
||||
_, err := dbWall.GetWall()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(fakeClock.NowCallCount()).To(Equal(2))
|
||||
Expect(fakeClock.UntilCallCount()).To(Equal(1))
|
||||
})
|
||||
|
||||
Specify("with the TTL field set", func() {
|
||||
_ = dbWall.SetWall(msgWithTTL)
|
||||
|
||||
actualWall, _ := dbWall.GetWall()
|
||||
msgWithTTL.TTL = 30 * time.Second
|
||||
Expect(actualWall).To(Equal(msgWithTTL))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Context("the message has expired", func() {
|
||||
It("returns no message", func() {
|
||||
_ = dbWall.SetWall(msgWithTTL)
|
||||
fakeClock.NowReturns(startTime.Add(time.Hour))
|
||||
|
||||
actualWall, err := dbWall.GetWall()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(fakeClock.NowCallCount()).To(Equal(2))
|
||||
Expect(actualWall).To(Equal(atc.Wall{}))
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Context("multiple messages are set", func() {
|
||||
It("returns the last message that was set", func() {
|
||||
expectedWall := atc.Wall{Message: "test 3"}
|
||||
dbWall.SetWall(atc.Wall{Message: "test 1"})
|
||||
dbWall.SetWall(atc.Wall{Message: "test 2"})
|
||||
dbWall.SetWall(expectedWall)
|
||||
|
||||
actualWall, err := dbWall.GetWall()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(actualWall).To(Equal(expectedWall))
|
||||
})
|
||||
})
|
||||
|
||||
Context("clearing the wall", func() {
|
||||
BeforeEach(func() {
|
||||
dbWall.SetWall(msgOnly)
|
||||
actualWall, err := dbWall.GetWall()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(actualWall).To(Equal(msgOnly), "ensure the message has been set before proceeding")
|
||||
})
|
||||
It("returns no error", func() {
|
||||
err := dbWall.Clear()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
})
|
||||
It("GetWall returns no message after clearing the wall", func() {
|
||||
_ = dbWall.Clear()
|
||||
|
||||
actualWall, err := dbWall.GetWall()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(actualWall).To(Equal(atc.Wall{}))
|
||||
})
|
||||
})
|
||||
})
|
|
@ -104,6 +104,10 @@ const (
|
|||
ListBuildArtifacts = "ListBuildArtifacts"
|
||||
|
||||
ListActiveUsersSince = "ListActiveUsersSince"
|
||||
|
||||
SetWall = "SetWall"
|
||||
GetWall = "GetWall"
|
||||
ClearWall = "ClearWall"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -214,4 +218,8 @@ var Routes = rata.Routes([]rata.Route{
|
|||
|
||||
{Path: "/api/v1/teams/:team_name/artifacts", Method: "POST", Name: CreateArtifact},
|
||||
{Path: "/api/v1/teams/:team_name/artifacts/:artifact_id", Method: "GET", Name: GetArtifact},
|
||||
|
||||
{Path: "/api/v1/wall", Method: "GET", Name: GetWall},
|
||||
{Path: "/api/v1/wall", Method: "PUT", Name: SetWall},
|
||||
{Path: "/api/v1/wall", Method: "DELETE", Name: ClearWall},
|
||||
})
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
package atc
|
||||
|
||||
import "time"
|
||||
|
||||
type Wall struct {
|
||||
Message string `json:"message,omitempty"`
|
||||
TTL time.Duration `json:"TTL,omitempty"`
|
||||
}
|
|
@ -109,13 +109,16 @@ func (wrappa *APIAuthWrappa) Wrap(handlers rata.Handlers) rata.Handlers {
|
|||
atc.ListAllJobs,
|
||||
atc.ListAllResources,
|
||||
atc.ListBuilds,
|
||||
atc.MainJobBadge:
|
||||
atc.MainJobBadge,
|
||||
atc.GetWall:
|
||||
newHandler = auth.CheckAuthenticationIfProvidedHandler(handler, rejector)
|
||||
|
||||
case atc.GetLogLevel,
|
||||
atc.ListActiveUsersSince,
|
||||
atc.SetLogLevel,
|
||||
atc.GetInfoCreds:
|
||||
atc.GetInfoCreds,
|
||||
atc.SetWall,
|
||||
atc.ClearWall:
|
||||
newHandler = auth.CheckAdminHandler(handler, rejector)
|
||||
|
||||
// authorized (requested team matches resource team)
|
||||
|
|
|
@ -211,12 +211,15 @@ var _ = Describe("APIAuthWrappa", func() {
|
|||
atc.ListAllResources: authenticateIfTokenProvided(inputHandlers[atc.ListAllResources]),
|
||||
atc.ListTeams: authenticateIfTokenProvided(inputHandlers[atc.ListTeams]),
|
||||
atc.MainJobBadge: authenticateIfTokenProvided(inputHandlers[atc.MainJobBadge]),
|
||||
atc.GetWall: authenticateIfTokenProvided(inputHandlers[atc.GetWall]),
|
||||
|
||||
// authenticated and is admin
|
||||
atc.GetLogLevel: authenticatedAndAdmin(inputHandlers[atc.GetLogLevel]),
|
||||
atc.SetLogLevel: authenticatedAndAdmin(inputHandlers[atc.SetLogLevel]),
|
||||
atc.GetInfoCreds: authenticatedAndAdmin(inputHandlers[atc.GetInfoCreds]),
|
||||
atc.ListActiveUsersSince: authenticatedAndAdmin(inputHandlers[atc.ListActiveUsersSince]),
|
||||
atc.SetWall: authenticatedAndAdmin(inputHandlers[atc.SetWall]),
|
||||
atc.ClearWall: authenticatedAndAdmin(inputHandlers[atc.ClearWall]),
|
||||
|
||||
// authorized (requested team matches resource team)
|
||||
atc.CheckResource: authorized(inputHandlers[atc.CheckResource]),
|
||||
|
|
Loading…
Reference in New Issue