Merge pull request #5113 from Pix4D/pipelines_last_updated

This commit is contained in:
Rui Yang 2020-03-03 14:08:02 -05:00 committed by GitHub
commit 1ae69250ce
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 176 additions and 57 deletions

View File

@ -47,6 +47,7 @@ var _ = Describe("Pipelines API", func() {
Resources: []string{"resource3", "resource4"},
},
})
publicPipeline.LastUpdatedReturns(time.Unix(1, 0))
anotherPublicPipeline = new(dbfakes.FakePipeline)
anotherPublicPipeline.IDReturns(2)
@ -54,6 +55,7 @@ var _ = Describe("Pipelines API", func() {
anotherPublicPipeline.PublicReturns(true)
anotherPublicPipeline.TeamNameReturns("another")
anotherPublicPipeline.NameReturns("another-pipeline")
anotherPublicPipeline.LastUpdatedReturns(time.Unix(1, 0))
privatePipeline = new(dbfakes.FakePipeline)
privatePipeline.IDReturns(3)
@ -68,6 +70,7 @@ var _ = Describe("Pipelines API", func() {
Resources: []string{"resource1", "resource2"},
},
})
privatePipeline.LastUpdatedReturns(time.Unix(1, 0))
privatePipelineFromAnotherTeam = new(dbfakes.FakePipeline)
privatePipelineFromAnotherTeam.IDReturns(3)
@ -75,6 +78,7 @@ var _ = Describe("Pipelines API", func() {
privatePipelineFromAnotherTeam.PublicReturns(false)
privatePipelineFromAnotherTeam.TeamNameReturns("main")
privatePipelineFromAnotherTeam.NameReturns("private-pipeline")
privatePipelineFromAnotherTeam.LastUpdatedReturns(time.Unix(1, 0))
fakeTeam.PipelinesReturns([]db.Pipeline{
privatePipeline,
@ -134,6 +138,7 @@ var _ = Describe("Pipelines API", func() {
"paused": true,
"public": true,
"team_name": "main",
"last_updated": 1,
"groups": [
{
"name": "group2",
@ -147,7 +152,8 @@ var _ = Describe("Pipelines API", func() {
"name": "another-pipeline",
"paused": true,
"public": true,
"team_name": "another"
"team_name": "another",
"last_updated": 1
}]`))
})
It("populates pipeline factory with no team names", func() {
@ -175,6 +181,7 @@ var _ = Describe("Pipelines API", func() {
"paused": false,
"public": false,
"team_name": "main",
"last_updated": 1,
"groups": [
{
"name": "group1",
@ -189,6 +196,7 @@ var _ = Describe("Pipelines API", func() {
"paused": true,
"public": true,
"team_name": "main",
"last_updated": 1,
"groups": [
{
"name": "group2",
@ -202,7 +210,8 @@ var _ = Describe("Pipelines API", func() {
"name": "another-pipeline",
"paused": true,
"public": true,
"team_name": "another"
"team_name": "another",
"last_updated": 1
}]`))
})
@ -283,6 +292,7 @@ var _ = Describe("Pipelines API", func() {
"paused": false,
"public": false,
"team_name": "main",
"last_updated": 1,
"groups": [
{
"name": "group1",
@ -297,6 +307,7 @@ var _ = Describe("Pipelines API", func() {
"paused": true,
"public": true,
"team_name": "main",
"last_updated": 1,
"groups": [
{
"name": "group2",
@ -335,6 +346,7 @@ var _ = Describe("Pipelines API", func() {
"paused": true,
"public": true,
"team_name": "main",
"last_updated": 1,
"groups": [
{
"name": "group2",
@ -363,6 +375,7 @@ var _ = Describe("Pipelines API", func() {
"paused": true,
"public": true,
"team_name": "main",
"last_updated": 1,
"groups": [
{
"name": "group2",
@ -398,6 +411,7 @@ var _ = Describe("Pipelines API", func() {
Resources: []string{"resource3", "resource4"},
},
})
fakePipeline.LastUpdatedReturns(time.Unix(1, 0))
})
JustBeforeEach(func() {
@ -446,6 +460,7 @@ var _ = Describe("Pipelines API", func() {
"paused": false,
"public": true,
"team_name": "a-team",
"last_updated": 1,
"groups": [
{
"name": "group1",

View File

@ -7,11 +7,12 @@ import (
func Pipeline(savedPipeline db.Pipeline) atc.Pipeline {
return atc.Pipeline{
ID: savedPipeline.ID(),
Name: savedPipeline.Name(),
TeamName: savedPipeline.TeamName(),
Paused: savedPipeline.Paused(),
Public: savedPipeline.Public(),
Groups: savedPipeline.Groups(),
ID: savedPipeline.ID(),
Name: savedPipeline.Name(),
TeamName: savedPipeline.TeamName(),
Paused: savedPipeline.Paused(),
Public: savedPipeline.Public(),
Groups: savedPipeline.Groups(),
LastUpdated: savedPipeline.LastUpdated().Unix(),
}
}

View File

@ -3,6 +3,7 @@ package dbfakes
import (
"sync"
"time"
"code.cloudfoundry.org/lager"
"github.com/concourse/concourse/atc"
@ -242,6 +243,16 @@ type FakePipeline struct {
result1 db.Jobs
result2 error
}
LastUpdatedStub func() time.Time
lastUpdatedMutex sync.RWMutex
lastUpdatedArgsForCall []struct {
}
lastUpdatedReturns struct {
result1 time.Time
}
lastUpdatedReturnsOnCall map[int]struct {
result1 time.Time
}
LoadDebugVersionsDBStub func() (*atc.DebugVersionsDB, error)
loadDebugVersionsDBMutex sync.RWMutex
loadDebugVersionsDBArgsForCall []struct {
@ -1579,6 +1590,58 @@ func (fake *FakePipeline) JobsReturnsOnCall(i int, result1 db.Jobs, result2 erro
}{result1, result2}
}
func (fake *FakePipeline) LastUpdated() time.Time {
fake.lastUpdatedMutex.Lock()
ret, specificReturn := fake.lastUpdatedReturnsOnCall[len(fake.lastUpdatedArgsForCall)]
fake.lastUpdatedArgsForCall = append(fake.lastUpdatedArgsForCall, struct {
}{})
fake.recordInvocation("LastUpdated", []interface{}{})
fake.lastUpdatedMutex.Unlock()
if fake.LastUpdatedStub != nil {
return fake.LastUpdatedStub()
}
if specificReturn {
return ret.result1
}
fakeReturns := fake.lastUpdatedReturns
return fakeReturns.result1
}
func (fake *FakePipeline) LastUpdatedCallCount() int {
fake.lastUpdatedMutex.RLock()
defer fake.lastUpdatedMutex.RUnlock()
return len(fake.lastUpdatedArgsForCall)
}
func (fake *FakePipeline) LastUpdatedCalls(stub func() time.Time) {
fake.lastUpdatedMutex.Lock()
defer fake.lastUpdatedMutex.Unlock()
fake.LastUpdatedStub = stub
}
func (fake *FakePipeline) LastUpdatedReturns(result1 time.Time) {
fake.lastUpdatedMutex.Lock()
defer fake.lastUpdatedMutex.Unlock()
fake.LastUpdatedStub = nil
fake.lastUpdatedReturns = struct {
result1 time.Time
}{result1}
}
func (fake *FakePipeline) LastUpdatedReturnsOnCall(i int, result1 time.Time) {
fake.lastUpdatedMutex.Lock()
defer fake.lastUpdatedMutex.Unlock()
fake.LastUpdatedStub = nil
if fake.lastUpdatedReturnsOnCall == nil {
fake.lastUpdatedReturnsOnCall = make(map[int]struct {
result1 time.Time
})
}
fake.lastUpdatedReturnsOnCall[i] = struct {
result1 time.Time
}{result1}
}
func (fake *FakePipeline) LoadDebugVersionsDB() (*atc.DebugVersionsDB, error) {
fake.loadDebugVersionsDBMutex.Lock()
ret, specificReturn := fake.loadDebugVersionsDBReturnsOnCall[len(fake.loadDebugVersionsDBArgsForCall)]
@ -2711,6 +2774,8 @@ func (fake *FakePipeline) Invocations() map[string][][]interface{} {
defer fake.jobMutex.RUnlock()
fake.jobsMutex.RLock()
defer fake.jobsMutex.RUnlock()
fake.lastUpdatedMutex.RLock()
defer fake.lastUpdatedMutex.RUnlock()
fake.loadDebugVersionsDBMutex.RLock()
defer fake.loadDebugVersionsDBMutex.RUnlock()
fake.nameMutex.RLock()

View File

@ -0,0 +1,4 @@
BEGIN;
ALTER TABLE pipelines
DROP COLUMN "last_updated";
COMMIT;

View File

@ -0,0 +1,4 @@
BEGIN;
ALTER TABLE pipelines
ADD COLUMN "last_updated" timestamp with time zone NOT NULL DEFAULT '1970-01-01 00:00:00';
COMMIT;

View File

@ -6,6 +6,7 @@ import (
"fmt"
"strconv"
"strings"
"time"
"code.cloudfoundry.org/lager"
@ -45,6 +46,7 @@ type Pipeline interface {
Config() (atc.Config, error)
Public() bool
Paused() bool
LastUpdated() time.Time
CheckPaused() (bool, error)
Reload() (bool, error)
@ -99,6 +101,7 @@ type pipeline struct {
configVersion ConfigVersion
paused bool
public bool
lastUpdated time.Time
conn Conn
lockFactory lock.LockFactory
@ -117,7 +120,8 @@ var pipelinesQuery = psql.Select(`
p.team_id,
t.name,
p.paused,
p.public
p.public,
p.last_updated
`).
From("pipelines p").
LeftJoin("teams t ON p.team_id = t.id")
@ -139,6 +143,7 @@ func (p *pipeline) VarSources() atc.VarSourceConfigs { return p.varSources }
func (p *pipeline) ConfigVersion() ConfigVersion { return p.configVersion }
func (p *pipeline) Public() bool { return p.public }
func (p *pipeline) Paused() bool { return p.paused }
func (p *pipeline) LastUpdated() time.Time { return p.lastUpdated }
// IMPORTANT: This method is broken with the new resource config versions changes
func (p *pipeline) Causality(versionedResourceID int) ([]Cause, error) {

View File

@ -382,14 +382,15 @@ func (t *team) SavePipeline(
if !existingConfig {
err = psql.Insert("pipelines").
SetMap(map[string]interface{}{
"name": pipelineName,
"groups": groupsPayload,
"var_sources": encryptedVarSourcesPayload,
"nonce": nonce,
"version": sq.Expr("nextval('config_version_seq')"),
"ordering": sq.Expr("currval('pipelines_id_seq')"),
"paused": initiallyPaused,
"team_id": t.id,
"name": pipelineName,
"groups": groupsPayload,
"var_sources": encryptedVarSourcesPayload,
"nonce": nonce,
"version": sq.Expr("nextval('config_version_seq')"),
"ordering": sq.Expr("currval('pipelines_id_seq')"),
"paused": initiallyPaused,
"last_updated": sq.Expr("now()"),
"team_id": t.id,
}).
Suffix("RETURNING id").
RunWith(tx).
@ -403,6 +404,7 @@ func (t *team) SavePipeline(
Set("var_sources", encryptedVarSourcesPayload).
Set("nonce", nonce).
Set("version", sq.Expr("nextval('config_version_seq')")).
Set("last_updated", sq.Expr("now()")).
Where(sq.Eq{
"name": pipelineName,
"version": from,
@ -1090,16 +1092,19 @@ func (t *team) findContainer(whereClause sq.Sqlizer) (CreatingContainer, Created
func scanPipeline(p *pipeline, scan scannable) error {
var (
groups sql.NullString
varSources sql.NullString
nonce sql.NullString
nonceStr *string
groups sql.NullString
varSources sql.NullString
nonce sql.NullString
nonceStr *string
lastUpdated pq.NullTime
)
err := scan.Scan(&p.id, &p.name, &groups, &varSources, &nonce, &p.configVersion, &p.teamID, &p.teamName, &p.paused, &p.public)
err := scan.Scan(&p.id, &p.name, &groups, &varSources, &nonce, &p.configVersion, &p.teamID, &p.teamName, &p.paused, &p.public, &lastUpdated)
if err != nil {
return err
}
p.lastUpdated = lastUpdated.Time
if groups.Valid {
var pipelineGroups atc.GroupConfigs
err = json.Unmarshal([]byte(groups.String), &pipelineGroups)

View File

@ -1,12 +1,13 @@
package atc
type Pipeline struct {
ID int `json:"id"`
Name string `json:"name"`
Paused bool `json:"paused"`
Public bool `json:"public"`
Groups GroupConfigs `json:"groups,omitempty"`
TeamName string `json:"team_name"`
ID int `json:"id"`
Name string `json:"name"`
Paused bool `json:"paused"`
Public bool `json:"public"`
Groups GroupConfigs `json:"groups,omitempty"`
TeamName string `json:"team_name"`
LastUpdated int64 `json:"last_updated,omitempty"`
}
type RenameRequest struct {

View File

@ -2,6 +2,7 @@ package commands
import (
"os"
"time"
"github.com/concourse/concourse/atc"
"github.com/concourse/concourse/fly/commands/internal/displayhelpers"
@ -31,10 +32,10 @@ func (command *PipelinesCommand) Execute([]string) error {
if command.All {
pipelines, err = target.Client().ListPipelines()
headers = []string{"name", "team", "paused", "public"}
headers = []string{"name", "team", "paused", "public", "last updated"}
} else {
pipelines, err = target.Team().ListPipelines()
headers = []string{"name", "paused", "public"}
headers = []string{"name", "paused", "public", "last updated"}
}
if err != nil {
return err
@ -77,6 +78,7 @@ func (command *PipelinesCommand) Execute([]string) error {
}
row = append(row, pausedColumn)
row = append(row, publicColumn)
row = append(row, ui.TableCell{Contents: time.Unix(p.LastUpdated, 0).String()})
table.Data = append(table.Data, row)
}

View File

@ -3,6 +3,7 @@ package integration_test
import (
"os"
"os/exec"
"time"
"github.com/concourse/concourse/atc"
"github.com/concourse/concourse/fly/ui"
@ -28,9 +29,9 @@ var _ = Describe("Fly CLI", func() {
ghttp.CombineHandlers(
ghttp.VerifyRequest("GET", "/api/v1/teams/main/pipelines"),
ghttp.RespondWithJSONEncoded(200, []atc.Pipeline{
{Name: "pipeline-1-longer", Paused: false, Public: false},
{Name: "pipeline-2", Paused: true, Public: false},
{Name: "pipeline-3", Paused: false, Public: true},
{Name: "pipeline-1-longer", Paused: false, Public: false, LastUpdated: 1},
{Name: "pipeline-2", Paused: true, Public: false, LastUpdated: 1},
{Name: "pipeline-3", Paused: false, Public: true, LastUpdated: 1},
}),
),
)
@ -52,21 +53,24 @@ var _ = Describe("Fly CLI", func() {
"name": "pipeline-1-longer",
"paused": false,
"public": false,
"team_name": ""
"team_name": "",
"last_updated": 1
},
{
"id": 0,
"name": "pipeline-2",
"paused": true,
"public": false,
"team_name": ""
"team_name": "",
"last_updated": 1
},
{
"id": 0,
"name": "pipeline-3",
"paused": false,
"public": true,
"team_name": ""
"team_name": "",
"last_updated": 1
}
]`))
})
@ -82,11 +86,12 @@ var _ = Describe("Fly CLI", func() {
{Contents: "name", Color: color.New(color.Bold)},
{Contents: "paused", Color: color.New(color.Bold)},
{Contents: "public", Color: color.New(color.Bold)},
{Contents: "last updated", Color: color.New(color.Bold)},
},
Data: []ui.TableRow{
{{Contents: "pipeline-1-longer"}, {Contents: "no"}, {Contents: "no"}},
{{Contents: "pipeline-2"}, {Contents: "yes", Color: color.New(color.FgCyan)}, {Contents: "no"}},
{{Contents: "pipeline-3"}, {Contents: "no"}, {Contents: "yes", Color: color.New(color.FgCyan)}},
{{Contents: "pipeline-1-longer"}, {Contents: "no"}, {Contents: "no"}, {Contents: time.Unix(1, 0).String()}},
{{Contents: "pipeline-2"}, {Contents: "yes", Color: color.New(color.FgCyan)}, {Contents: "no"}, {Contents: time.Unix(1, 0).String()}},
{{Contents: "pipeline-3"}, {Contents: "no"}, {Contents: "yes", Color: color.New(color.FgCyan)}, {Contents: time.Unix(1, 0).String()}},
},
}))
})
@ -99,11 +104,11 @@ var _ = Describe("Fly CLI", func() {
ghttp.CombineHandlers(
ghttp.VerifyRequest("GET", "/api/v1/pipelines"),
ghttp.RespondWithJSONEncoded(200, []atc.Pipeline{
{Name: "pipeline-1-longer", Paused: false, Public: false, TeamName: "main"},
{Name: "pipeline-2", Paused: true, Public: false, TeamName: "main"},
{Name: "pipeline-3", Paused: false, Public: true, TeamName: "main"},
{Name: "foreign-pipeline-1", Paused: false, Public: true, TeamName: "other"},
{Name: "foreign-pipeline-2", Paused: false, Public: true, TeamName: "other"},
{Name: "pipeline-1-longer", Paused: false, Public: false, TeamName: "main", LastUpdated: 1},
{Name: "pipeline-2", Paused: true, Public: false, TeamName: "main", LastUpdated: 1},
{Name: "pipeline-3", Paused: false, Public: true, TeamName: "main", LastUpdated: 1},
{Name: "foreign-pipeline-1", Paused: false, Public: true, TeamName: "other", LastUpdated: 1},
{Name: "foreign-pipeline-2", Paused: false, Public: true, TeamName: "other", LastUpdated: 1},
}),
),
)
@ -125,35 +130,40 @@ var _ = Describe("Fly CLI", func() {
"name": "pipeline-1-longer",
"paused": false,
"public": false,
"team_name": "main"
"team_name": "main",
"last_updated": 1
},
{
"id": 0,
"name": "pipeline-2",
"paused": true,
"public": false,
"team_name": "main"
"team_name": "main",
"last_updated": 1
},
{
"id": 0,
"name": "pipeline-3",
"paused": false,
"public": true,
"team_name": "main"
"team_name": "main",
"last_updated": 1
},
{
"id": 0,
"name": "foreign-pipeline-1",
"paused": false,
"public": true,
"team_name": "other"
"team_name": "other",
"last_updated": 1
},
{
"id": 0,
"name": "foreign-pipeline-2",
"paused": false,
"public": true,
"team_name": "other"
"team_name": "other",
"last_updated": 1
}
]`))
})
@ -170,13 +180,14 @@ var _ = Describe("Fly CLI", func() {
{Contents: "team", Color: color.New(color.Bold)},
{Contents: "paused", Color: color.New(color.Bold)},
{Contents: "public", Color: color.New(color.Bold)},
{Contents: "last updated", Color: color.New(color.Bold)},
},
Data: []ui.TableRow{
{{Contents: "pipeline-1-longer"}, {Contents: "main"}, {Contents: "no"}, {Contents: "no"}},
{{Contents: "pipeline-2"}, {Contents: "main"}, {Contents: "yes", Color: color.New(color.FgCyan)}, {Contents: "no"}},
{{Contents: "pipeline-3"}, {Contents: "main"}, {Contents: "no"}, {Contents: "yes", Color: color.New(color.FgCyan)}},
{{Contents: "foreign-pipeline-1"}, {Contents: "other"}, {Contents: "no"}, {Contents: "yes", Color: color.New(color.FgCyan)}},
{{Contents: "foreign-pipeline-2"}, {Contents: "other"}, {Contents: "no"}, {Contents: "yes", Color: color.New(color.FgCyan)}},
{{Contents: "pipeline-1-longer"}, {Contents: "main"}, {Contents: "no"}, {Contents: "no"}, {Contents: time.Unix(1, 0).String()}},
{{Contents: "pipeline-2"}, {Contents: "main"}, {Contents: "yes", Color: color.New(color.FgCyan)}, {Contents: "no"}, {Contents: time.Unix(1, 0).String()}},
{{Contents: "pipeline-3"}, {Contents: "main"}, {Contents: "no"}, {Contents: "yes", Color: color.New(color.FgCyan)}, {Contents: time.Unix(1, 0).String()}},
{{Contents: "foreign-pipeline-1"}, {Contents: "other"}, {Contents: "no"}, {Contents: "yes", Color: color.New(color.FgCyan)}, {Contents: time.Unix(1, 0).String()}},
{{Contents: "foreign-pipeline-2"}, {Contents: "other"}, {Contents: "no"}, {Contents: "yes", Color: color.New(color.FgCyan)}, {Contents: time.Unix(1, 0).String()}},
},
}))
})
@ -190,9 +201,9 @@ var _ = Describe("Fly CLI", func() {
ghttp.CombineHandlers(
ghttp.VerifyRequest("GET", "/api/v1/teams/main/pipelines"),
ghttp.RespondWithJSONEncoded(200, []atc.Pipeline{
{Name: "some-pipeline-1", Paused: false, Public: false},
{Name: "some-pipeline-2", Paused: false, Public: false},
{Name: "another-pipeline", Paused: false, Public: false},
{Name: "some-pipeline-1", Paused: false, Public: false, LastUpdated: 1},
{Name: "some-pipeline-2", Paused: false, Public: false, LastUpdated: 1},
{Name: "another-pipeline", Paused: false, Public: false, LastUpdated: 1},
}),
),
)

View File

@ -64,3 +64,9 @@ errors from the library we used before.
[OpenTelemetry]: https://opentelemetry.io/
[Jaeger]: https://www.jaegertracing.io/
[Stackdriver]: https://cloud.google.com/trace/
#### <sub><sup><a name="5113" href="#5113">:link:</a></sup></sub> feature
* @aledeganopix4d added a `last updated` column to the output of `fly pipelines` showing
the last date where the pipeline was set or reset. #5113