mirror of
https://github.com/harness/drone.git
synced 2025-05-21 19:39:59 +08:00
Merge pull request #1954 from bradrydzewski/master
implement jsonrpc2 agent behind feature flag
This commit is contained in:
commit
fe669b744c
@ -4,7 +4,7 @@ workspace:
|
|||||||
|
|
||||||
pipeline:
|
pipeline:
|
||||||
test:
|
test:
|
||||||
image: golang:1.6
|
image: golang:1.8
|
||||||
environment:
|
environment:
|
||||||
- GO15VENDOREXPERIMENT=1
|
- GO15VENDOREXPERIMENT=1
|
||||||
commands:
|
commands:
|
||||||
@ -12,7 +12,7 @@ pipeline:
|
|||||||
- make test test_postgres test_mysql
|
- make test test_postgres test_mysql
|
||||||
|
|
||||||
compile:
|
compile:
|
||||||
image: golang:1.6
|
image: golang:1.8
|
||||||
environment:
|
environment:
|
||||||
- GO15VENDOREXPERIMENT=1
|
- GO15VENDOREXPERIMENT=1
|
||||||
- GOPATH=/go
|
- GOPATH=/go
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
package agent
|
package agent
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"strings"
|
"strings"
|
||||||
@ -60,7 +62,7 @@ var AgentCmd = cli.Command{
|
|||||||
Value: "amd64",
|
Value: "amd64",
|
||||||
},
|
},
|
||||||
cli.StringFlag{
|
cli.StringFlag{
|
||||||
EnvVar: "DRONE_SERVER",
|
EnvVar: "DRONE_SERVER,DRONE_ENDPOINT",
|
||||||
Name: "drone-server",
|
Name: "drone-server",
|
||||||
Usage: "drone server address",
|
Usage: "drone server address",
|
||||||
Value: "ws://localhost:8000/ws/broker",
|
Value: "ws://localhost:8000/ws/broker",
|
||||||
@ -138,11 +140,55 @@ var AgentCmd = cli.Command{
|
|||||||
Name: "extension",
|
Name: "extension",
|
||||||
Usage: "custom plugin extension endpoint",
|
Usage: "custom plugin extension endpoint",
|
||||||
},
|
},
|
||||||
|
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
|
||||||
|
cli.BoolFlag{
|
||||||
|
EnvVar: "DRONE_CANARY",
|
||||||
|
Name: "canary",
|
||||||
|
Usage: "enable experimental features at your own risk",
|
||||||
|
},
|
||||||
|
|
||||||
|
// cli.StringFlag{
|
||||||
|
// Name: "endpoint",
|
||||||
|
// EnvVar: "DRONE_ENDPOINT,DRONE_SERVER",
|
||||||
|
// Value: "ws://localhost:9999/ws/rpc",
|
||||||
|
// },
|
||||||
|
// cli.DurationFlag{
|
||||||
|
// Name: "backoff",
|
||||||
|
// EnvVar: "DRONE_BACKOFF",
|
||||||
|
// Value: time.Second * 15,
|
||||||
|
// },
|
||||||
|
cli.IntFlag{
|
||||||
|
Name: "retry-limit",
|
||||||
|
EnvVar: "DRONE_RETRY_LIMIT",
|
||||||
|
Value: math.MaxInt32,
|
||||||
|
},
|
||||||
|
cli.IntFlag{
|
||||||
|
Name: "max-procs",
|
||||||
|
EnvVar: "DRONE_MAX_PROCS",
|
||||||
|
Value: 1,
|
||||||
|
},
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "platform",
|
||||||
|
EnvVar: "DRONE_PLATFORM",
|
||||||
|
Value: "linux/amd64",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func start(c *cli.Context) {
|
func start(c *cli.Context) {
|
||||||
|
|
||||||
|
if c.Bool("canary") {
|
||||||
|
if err := loop(c); err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
log := redlog.New(os.Stderr)
|
log := redlog.New(os.Stderr)
|
||||||
log.SetLevel(0)
|
log.SetLevel(0)
|
||||||
logger.SetLogger(log)
|
logger.SetLogger(log)
|
||||||
@ -187,7 +233,7 @@ func start(c *cli.Context) {
|
|||||||
client.Ack(m.Ack)
|
client.Ack(m.Ack)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
r := pipeline{
|
r := pipelinet{
|
||||||
drone: client,
|
drone: client,
|
||||||
docker: docker,
|
docker: docker,
|
||||||
config: config{
|
config: config{
|
||||||
|
@ -22,13 +22,13 @@ type config struct {
|
|||||||
extension []string
|
extension []string
|
||||||
}
|
}
|
||||||
|
|
||||||
type pipeline struct {
|
type pipelinet struct {
|
||||||
drone *stomp.Client
|
drone *stomp.Client
|
||||||
docker dockerclient.Client
|
docker dockerclient.Client
|
||||||
config config
|
config config
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *pipeline) run(w *model.Work) {
|
func (r *pipelinet) run(w *model.Work) {
|
||||||
|
|
||||||
// defer func() {
|
// defer func() {
|
||||||
// // r.drone.Ack(id, opts)
|
// // r.drone.Ack(id, opts)
|
||||||
|
206
drone/agent/exp.go
Normal file
206
drone/agent/exp.go
Normal file
@ -0,0 +1,206 @@
|
|||||||
|
package agent
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"net/url"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/cncd/pipeline/pipeline"
|
||||||
|
"github.com/cncd/pipeline/pipeline/backend"
|
||||||
|
"github.com/cncd/pipeline/pipeline/backend/docker"
|
||||||
|
"github.com/cncd/pipeline/pipeline/interrupt"
|
||||||
|
"github.com/cncd/pipeline/pipeline/multipart"
|
||||||
|
"github.com/cncd/pipeline/pipeline/rpc"
|
||||||
|
|
||||||
|
"github.com/codegangsta/cli"
|
||||||
|
"github.com/tevino/abool"
|
||||||
|
)
|
||||||
|
|
||||||
|
func loop(c *cli.Context) error {
|
||||||
|
endpoint, err := url.Parse(
|
||||||
|
c.String("drone-server"),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
filter := rpc.Filter{
|
||||||
|
Labels: map[string]string{
|
||||||
|
"platform": c.String("platform"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
client, err := rpc.NewClient(
|
||||||
|
endpoint.String(),
|
||||||
|
rpc.WithRetryLimit(
|
||||||
|
c.Int("retry-limit"),
|
||||||
|
),
|
||||||
|
rpc.WithBackoff(
|
||||||
|
c.Duration("backoff"),
|
||||||
|
),
|
||||||
|
rpc.WithToken(
|
||||||
|
c.String("drone-secret"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer client.Close()
|
||||||
|
|
||||||
|
sigterm := abool.New()
|
||||||
|
ctx := context.Background()
|
||||||
|
ctx = interrupt.WithContextFunc(ctx, func() {
|
||||||
|
println("ctrl+c received, terminating process")
|
||||||
|
sigterm.Set()
|
||||||
|
})
|
||||||
|
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
parallel := c.Int("max-procs")
|
||||||
|
wg.Add(parallel)
|
||||||
|
|
||||||
|
for i := 0; i < parallel; i++ {
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
for {
|
||||||
|
if sigterm.IsSet() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := run(ctx, client, filter); err != nil {
|
||||||
|
log.Printf("build runner encountered error: exiting: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
wg.Wait()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
maxFileUpload = 5000000
|
||||||
|
maxLogsUpload = 5000000
|
||||||
|
)
|
||||||
|
|
||||||
|
func run(ctx context.Context, client rpc.Peer, filter rpc.Filter) error {
|
||||||
|
log.Println("pipeline: request next execution")
|
||||||
|
|
||||||
|
// get the next job from the queue
|
||||||
|
work, err := client.Next(ctx, filter)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if work == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
log.Printf("pipeline: received next execution: %s", work.ID)
|
||||||
|
|
||||||
|
// new docker engine
|
||||||
|
engine, err := docker.NewEnv()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
timeout := time.Hour
|
||||||
|
if minutes := work.Timeout; minutes != 0 {
|
||||||
|
timeout = time.Duration(minutes) * time.Minute
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
cancelled := abool.New()
|
||||||
|
go func() {
|
||||||
|
if werr := client.Wait(ctx, work.ID); werr != nil {
|
||||||
|
cancelled.SetTo(true)
|
||||||
|
log.Printf("pipeline: cancel signal received: %s: %s", work.ID, werr)
|
||||||
|
cancel()
|
||||||
|
} else {
|
||||||
|
log.Printf("pipeline: cancel channel closed: %s", work.ID)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
log.Printf("pipeline: cancel ping loop: %s", work.ID)
|
||||||
|
return
|
||||||
|
case <-time.After(time.Minute):
|
||||||
|
log.Printf("pipeline: ping queue: %s", work.ID)
|
||||||
|
client.Extend(ctx, work.ID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
state := rpc.State{}
|
||||||
|
state.Started = time.Now().Unix()
|
||||||
|
err = client.Update(context.Background(), work.ID, state)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("pipeline: error updating pipeline status: %s: %s", work.ID, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var uploads sync.WaitGroup
|
||||||
|
defaultLogger := pipeline.LogFunc(func(proc *backend.Step, rc multipart.Reader) error {
|
||||||
|
part, rerr := rc.NextPart()
|
||||||
|
if rerr != nil {
|
||||||
|
return rerr
|
||||||
|
}
|
||||||
|
uploads.Add(1)
|
||||||
|
writer := rpc.NewLineWriter(client, work.ID, proc.Alias)
|
||||||
|
rlimit := io.LimitReader(part, maxLogsUpload)
|
||||||
|
io.Copy(writer, rlimit)
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
log.Printf("pipeline: finish uploading logs: %s: step %s", work.ID, proc.Alias)
|
||||||
|
uploads.Done()
|
||||||
|
}()
|
||||||
|
|
||||||
|
part, rerr = rc.NextPart()
|
||||||
|
if rerr != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
rlimit = io.LimitReader(part, maxFileUpload)
|
||||||
|
mime := part.Header().Get("Content-Type")
|
||||||
|
if serr := client.Upload(context.Background(), work.ID, mime, rlimit); serr != nil {
|
||||||
|
log.Printf("pipeline: cannot upload artifact: %s: %s: %s", work.ID, mime, serr)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
err = pipeline.New(work.Config,
|
||||||
|
pipeline.WithContext(ctx),
|
||||||
|
pipeline.WithLogger(defaultLogger),
|
||||||
|
pipeline.WithTracer(pipeline.DefaultTracer),
|
||||||
|
pipeline.WithEngine(engine),
|
||||||
|
).Run()
|
||||||
|
|
||||||
|
state.Finished = time.Now().Unix()
|
||||||
|
state.Exited = true
|
||||||
|
if err != nil {
|
||||||
|
state.Error = err.Error()
|
||||||
|
if xerr, ok := err.(*pipeline.ExitError); ok {
|
||||||
|
state.ExitCode = xerr.Code
|
||||||
|
}
|
||||||
|
if xerr, ok := err.(*pipeline.OomError); ok {
|
||||||
|
state.ExitCode = xerr.Code
|
||||||
|
}
|
||||||
|
if cancelled.IsSet() {
|
||||||
|
state.ExitCode = 137
|
||||||
|
} else if state.ExitCode == 0 {
|
||||||
|
state.ExitCode = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("pipeline: execution complete: %s", work.ID)
|
||||||
|
|
||||||
|
uploads.Wait()
|
||||||
|
err = client.Update(context.Background(), work.ID, state)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Pipeine: error updating pipeline status: %s: %s", work.ID, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
@ -8,6 +8,7 @@ type Build struct {
|
|||||||
Parent int `json:"parent" meddler:"build_parent"`
|
Parent int `json:"parent" meddler:"build_parent"`
|
||||||
Event string `json:"event" meddler:"build_event"`
|
Event string `json:"event" meddler:"build_event"`
|
||||||
Status string `json:"status" meddler:"build_status"`
|
Status string `json:"status" meddler:"build_status"`
|
||||||
|
Error string `json:"error" meddler:"build_error"`
|
||||||
Enqueued int64 `json:"enqueued_at" meddler:"build_enqueued"`
|
Enqueued int64 `json:"enqueued_at" meddler:"build_enqueued"`
|
||||||
Created int64 `json:"created_at" meddler:"build_created"`
|
Created int64 `json:"created_at" meddler:"build_created"`
|
||||||
Started int64 `json:"started_at" meddler:"build_started"`
|
Started int64 `json:"started_at" meddler:"build_started"`
|
||||||
|
@ -2,6 +2,7 @@ package router
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"os"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
|
||||||
@ -9,6 +10,7 @@ import (
|
|||||||
"github.com/drone/drone/router/middleware/session"
|
"github.com/drone/drone/router/middleware/session"
|
||||||
"github.com/drone/drone/router/middleware/token"
|
"github.com/drone/drone/router/middleware/token"
|
||||||
"github.com/drone/drone/server"
|
"github.com/drone/drone/server"
|
||||||
|
"github.com/drone/drone/server/debug"
|
||||||
"github.com/drone/drone/server/template"
|
"github.com/drone/drone/server/template"
|
||||||
|
|
||||||
"github.com/drone/drone-ui/dist"
|
"github.com/drone/drone-ui/dist"
|
||||||
@ -119,9 +121,15 @@ func Load(middleware ...gin.HandlerFunc) http.Handler {
|
|||||||
badges.GET("/cc.xml", server.GetCC)
|
badges.GET("/cc.xml", server.GetCC)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if os.Getenv("DRONE_CANARY") == "" {
|
||||||
e.POST("/hook", server.PostHook)
|
e.POST("/hook", server.PostHook)
|
||||||
e.POST("/api/hook", server.PostHook)
|
e.POST("/api/hook", server.PostHook)
|
||||||
|
} else {
|
||||||
|
e.POST("/hook", server.PostHook2)
|
||||||
|
e.POST("/api/hook", server.PostHook2)
|
||||||
|
}
|
||||||
|
|
||||||
|
if os.Getenv("DRONE_CANARY") == "" {
|
||||||
ws := e.Group("/ws")
|
ws := e.Group("/ws")
|
||||||
{
|
{
|
||||||
ws.GET("/broker", server.Broker)
|
ws.GET("/broker", server.Broker)
|
||||||
@ -133,6 +141,27 @@ func Load(middleware ...gin.HandlerFunc) http.Handler {
|
|||||||
server.LogStream,
|
server.LogStream,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
ws := e.Group("/ws")
|
||||||
|
{
|
||||||
|
ws.GET("/broker", server.RPCHandler)
|
||||||
|
ws.GET("/rpc", server.RPCHandler)
|
||||||
|
ws.GET("/feed", server.EventStream2)
|
||||||
|
ws.GET("/logs/:owner/:name/:build/:number",
|
||||||
|
session.SetRepo(),
|
||||||
|
session.SetPerm(),
|
||||||
|
session.MustPull,
|
||||||
|
server.LogStream2,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
info := e.Group("/api/info")
|
||||||
|
{
|
||||||
|
info.GET("/queue",
|
||||||
|
session.MustAdmin(),
|
||||||
|
server.GetQueueInfo,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
auth := e.Group("/authorize")
|
auth := e.Group("/authorize")
|
||||||
{
|
{
|
||||||
@ -147,41 +176,21 @@ func Load(middleware ...gin.HandlerFunc) http.Handler {
|
|||||||
builds.GET("", server.GetBuildQueue)
|
builds.GET("", server.GetBuildQueue)
|
||||||
}
|
}
|
||||||
|
|
||||||
agents := e.Group("/api/agents")
|
debugger := e.Group("/api/debug")
|
||||||
{
|
{
|
||||||
agents.Use(session.MustAdmin())
|
debugger.Use(session.MustAdmin())
|
||||||
agents.GET("", server.GetAgents)
|
debugger.GET("/pprof/", debug.IndexHandler())
|
||||||
|
debugger.GET("/pprof/heap", debug.HeapHandler())
|
||||||
|
debugger.GET("/pprof/goroutine", debug.GoroutineHandler())
|
||||||
|
debugger.GET("/pprof/block", debug.BlockHandler())
|
||||||
|
debugger.GET("/pprof/threadcreate", debug.ThreadCreateHandler())
|
||||||
|
debugger.GET("/pprof/cmdline", debug.CmdlineHandler())
|
||||||
|
debugger.GET("/pprof/profile", debug.ProfileHandler())
|
||||||
|
debugger.GET("/pprof/symbol", debug.SymbolHandler())
|
||||||
|
debugger.POST("/pprof/symbol", debug.SymbolHandler())
|
||||||
|
debugger.GET("/pprof/trace", debug.TraceHandler())
|
||||||
}
|
}
|
||||||
|
|
||||||
debug := e.Group("/api/debug")
|
|
||||||
{
|
|
||||||
debug.Use(session.MustAdmin())
|
|
||||||
debug.GET("/pprof/", server.IndexHandler())
|
|
||||||
debug.GET("/pprof/heap", server.HeapHandler())
|
|
||||||
debug.GET("/pprof/goroutine", server.GoroutineHandler())
|
|
||||||
debug.GET("/pprof/block", server.BlockHandler())
|
|
||||||
debug.GET("/pprof/threadcreate", server.ThreadCreateHandler())
|
|
||||||
debug.GET("/pprof/cmdline", server.CmdlineHandler())
|
|
||||||
debug.GET("/pprof/profile", server.ProfileHandler())
|
|
||||||
debug.GET("/pprof/symbol", server.SymbolHandler())
|
|
||||||
debug.POST("/pprof/symbol", server.SymbolHandler())
|
|
||||||
debug.GET("/pprof/trace", server.TraceHandler())
|
|
||||||
}
|
|
||||||
|
|
||||||
// DELETE THESE
|
|
||||||
// gitlab := e.Group("/gitlab/:owner/:name")
|
|
||||||
// {
|
|
||||||
// gitlab.Use(session.SetRepo())
|
|
||||||
// gitlab.GET("/commits/:sha", GetCommit)
|
|
||||||
// gitlab.GET("/pulls/:number", GetPullRequest)
|
|
||||||
//
|
|
||||||
// redirects := gitlab.Group("/redirect")
|
|
||||||
// {
|
|
||||||
// redirects.GET("/commits/:sha", RedirectSha)
|
|
||||||
// redirects.GET("/pulls/:number", RedirectPullRequest)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// bots := e.Group("/bots")
|
// bots := e.Group("/bots")
|
||||||
// {
|
// {
|
||||||
// bots.Use(session.MustUser())
|
// bots.Use(session.MustUser())
|
||||||
|
@ -1,15 +0,0 @@
|
|||||||
package server
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/drone/drone/store"
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
)
|
|
||||||
|
|
||||||
func GetAgents(c *gin.Context) {
|
|
||||||
agents, err := store.GetAgentList(c)
|
|
||||||
if err != nil {
|
|
||||||
c.String(500, "Error getting agent list. %s", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
c.JSON(200, agents)
|
|
||||||
}
|
|
236
server/build.go
236
server/build.go
@ -2,12 +2,19 @@ package server
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
log "github.com/Sirupsen/logrus"
|
log "github.com/Sirupsen/logrus"
|
||||||
|
"github.com/cncd/pipeline/pipeline/rpc"
|
||||||
|
"github.com/cncd/pubsub"
|
||||||
|
"github.com/cncd/queue"
|
||||||
"github.com/drone/drone/remote"
|
"github.com/drone/drone/remote"
|
||||||
"github.com/drone/drone/shared/httputil"
|
"github.com/drone/drone/shared/httputil"
|
||||||
"github.com/drone/drone/store"
|
"github.com/drone/drone/store"
|
||||||
@ -149,6 +156,7 @@ func DeleteBuild(c *gin.Context) {
|
|||||||
job.ExitCode = 137
|
job.ExitCode = 137
|
||||||
store.UpdateBuildJob(c, build, job)
|
store.UpdateBuildJob(c, build, job)
|
||||||
|
|
||||||
|
if os.Getenv("DRONE_CANARY") == "" {
|
||||||
client := stomp.MustFromContext(c)
|
client := stomp.MustFromContext(c)
|
||||||
client.SendJSON("/topic/cancel", model.Event{
|
client.SendJSON("/topic/cancel", model.Event{
|
||||||
Type: model.Cancelled,
|
Type: model.Cancelled,
|
||||||
@ -156,12 +164,19 @@ func DeleteBuild(c *gin.Context) {
|
|||||||
Build: *build,
|
Build: *build,
|
||||||
Job: *job,
|
Job: *job,
|
||||||
}, stomp.WithHeader("job-id", strconv.FormatInt(job.ID, 10)))
|
}, stomp.WithHeader("job-id", strconv.FormatInt(job.ID, 10)))
|
||||||
|
} else {
|
||||||
|
config.queue.Error(context.Background(), fmt.Sprint(job.ID), queue.ErrCancel)
|
||||||
|
}
|
||||||
c.String(204, "")
|
c.String(204, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
func PostBuild(c *gin.Context) {
|
func PostBuild(c *gin.Context) {
|
||||||
|
|
||||||
|
if os.Getenv("DRONE_CANARY") == "true" {
|
||||||
|
PostBuild2(c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
remote_ := remote.FromContext(c)
|
remote_ := remote.FromContext(c)
|
||||||
repo := session.Repo(c)
|
repo := session.Repo(c)
|
||||||
fork := c.DefaultQuery("fork", "false")
|
fork := c.DefaultQuery("fork", "false")
|
||||||
@ -197,8 +212,8 @@ func PostBuild(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// fetch the .drone.yml file from the database
|
// fetch the .drone.yml file from the database
|
||||||
config := ToConfig(c)
|
cfg := ToConfig(c)
|
||||||
raw, err := remote_.File(user, repo, build, config.Yaml)
|
raw, err := remote_.File(user, repo, build, cfg.Yaml)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("failure to get build config for %s. %s", repo.FullName, err)
|
log.Errorf("failure to get build config for %s. %s", repo.FullName, err)
|
||||||
c.AbortWithError(404, err)
|
c.AbortWithError(404, err)
|
||||||
@ -206,7 +221,7 @@ func PostBuild(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Fetch secrets file but don't exit on error as it's optional
|
// Fetch secrets file but don't exit on error as it's optional
|
||||||
sec, err := remote_.File(user, repo, build, config.Shasum)
|
sec, err := remote_.File(user, repo, build, cfg.Shasum)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debugf("cannot find build secrets for %s. %s", repo.FullName, err)
|
log.Debugf("cannot find build secrets for %s. %s", repo.FullName, err)
|
||||||
}
|
}
|
||||||
@ -275,6 +290,7 @@ func PostBuild(c *gin.Context) {
|
|||||||
build.Started = 0
|
build.Started = 0
|
||||||
build.Finished = 0
|
build.Finished = 0
|
||||||
build.Enqueued = time.Now().UTC().Unix()
|
build.Enqueued = time.Now().UTC().Unix()
|
||||||
|
build.Error = ""
|
||||||
for _, job := range jobs {
|
for _, job := range jobs {
|
||||||
for k, v := range buildParams {
|
for k, v := range buildParams {
|
||||||
job.Environment[k] = v
|
job.Environment[k] = v
|
||||||
@ -388,3 +404,215 @@ func copyLogs(dest io.Writer, src io.Reader) error {
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
|
||||||
|
func PostBuild2(c *gin.Context) {
|
||||||
|
|
||||||
|
remote_ := remote.FromContext(c)
|
||||||
|
repo := session.Repo(c)
|
||||||
|
fork := c.DefaultQuery("fork", "false")
|
||||||
|
|
||||||
|
num, err := strconv.Atoi(c.Param("number"))
|
||||||
|
if err != nil {
|
||||||
|
c.AbortWithError(http.StatusBadRequest, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
user, err := store.GetUser(c, repo.UserID)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("failure to find repo owner %s. %s", repo.FullName, err)
|
||||||
|
c.AbortWithError(500, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
build, err := store.GetBuildNumber(c, repo, num)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("failure to get build %d. %s", num, err)
|
||||||
|
c.AbortWithError(404, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the remote has a refresh token, the current access token
|
||||||
|
// may be stale. Therefore, we should refresh prior to dispatching
|
||||||
|
// the job.
|
||||||
|
if refresher, ok := remote_.(remote.Refresher); ok {
|
||||||
|
ok, _ := refresher.Refresh(user)
|
||||||
|
if ok {
|
||||||
|
store.UpdateUser(c, user)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fetch the .drone.yml file from the database
|
||||||
|
cfg := ToConfig(c)
|
||||||
|
raw, err := remote_.File(user, repo, build, cfg.Yaml)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("failure to get build config for %s. %s", repo.FullName, err)
|
||||||
|
c.AbortWithError(404, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
netrc, err := remote_.Netrc(user, repo)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("failure to generate netrc for %s. %s", repo.FullName, err)
|
||||||
|
c.AbortWithError(500, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
jobs, err := store.GetJobList(c, build)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("failure to get build %d jobs. %s", build.Number, err)
|
||||||
|
c.AbortWithError(404, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// must not restart a running build
|
||||||
|
if build.Status == model.StatusPending || build.Status == model.StatusRunning {
|
||||||
|
c.String(409, "Cannot re-start a started build")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// forking the build creates a duplicate of the build
|
||||||
|
// and then executes. This retains prior build history.
|
||||||
|
if forkit, _ := strconv.ParseBool(fork); forkit {
|
||||||
|
build.ID = 0
|
||||||
|
build.Number = 0
|
||||||
|
build.Parent = num
|
||||||
|
for _, job := range jobs {
|
||||||
|
job.ID = 0
|
||||||
|
job.NodeID = 0
|
||||||
|
}
|
||||||
|
err := store.CreateBuild(c, build, jobs...)
|
||||||
|
if err != nil {
|
||||||
|
c.String(500, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
event := c.DefaultQuery("event", build.Event)
|
||||||
|
if event == model.EventPush ||
|
||||||
|
event == model.EventPull ||
|
||||||
|
event == model.EventTag ||
|
||||||
|
event == model.EventDeploy {
|
||||||
|
build.Event = event
|
||||||
|
}
|
||||||
|
build.Deploy = c.DefaultQuery("deploy_to", build.Deploy)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read query string parameters into buildParams, exclude reserved params
|
||||||
|
var buildParams = map[string]string{}
|
||||||
|
for key, val := range c.Request.URL.Query() {
|
||||||
|
switch key {
|
||||||
|
case "fork", "event", "deploy_to":
|
||||||
|
default:
|
||||||
|
// We only accept string literals, because build parameters will be
|
||||||
|
// injected as environment variables
|
||||||
|
buildParams[key] = val[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// todo move this to database tier
|
||||||
|
// and wrap inside a transaction
|
||||||
|
build.Status = model.StatusPending
|
||||||
|
build.Started = 0
|
||||||
|
build.Finished = 0
|
||||||
|
build.Enqueued = time.Now().UTC().Unix()
|
||||||
|
build.Error = ""
|
||||||
|
for _, job := range jobs {
|
||||||
|
for k, v := range buildParams {
|
||||||
|
job.Environment[k] = v
|
||||||
|
}
|
||||||
|
job.Error = ""
|
||||||
|
job.Status = model.StatusPending
|
||||||
|
job.Started = 0
|
||||||
|
job.Finished = 0
|
||||||
|
job.ExitCode = 0
|
||||||
|
job.NodeID = 0
|
||||||
|
job.Enqueued = build.Enqueued
|
||||||
|
store.UpdateJob(c, job)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = store.UpdateBuild(c, build)
|
||||||
|
if err != nil {
|
||||||
|
c.AbortWithStatus(500)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(202, build)
|
||||||
|
|
||||||
|
// get the previous build so that we can send
|
||||||
|
// on status change notifications
|
||||||
|
last, _ := store.GetBuildLastBefore(c, repo, build.Branch, build.ID)
|
||||||
|
secs, err := store.GetMergedSecretList(c, repo)
|
||||||
|
if err != nil {
|
||||||
|
log.Debugf("Error getting secrets for %s#%d. %s", repo.FullName, build.Number, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
b := builder{
|
||||||
|
Repo: repo,
|
||||||
|
Curr: build,
|
||||||
|
Last: last,
|
||||||
|
Netrc: netrc,
|
||||||
|
Secs: secs,
|
||||||
|
Link: httputil.GetURL(c.Request),
|
||||||
|
Yaml: string(raw),
|
||||||
|
}
|
||||||
|
items, err := b.Build()
|
||||||
|
if err != nil {
|
||||||
|
build.Status = model.StatusError
|
||||||
|
build.Started = time.Now().Unix()
|
||||||
|
build.Finished = build.Started
|
||||||
|
build.Error = err.Error()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, item := range items {
|
||||||
|
// TODO prevent possible index out of bounds
|
||||||
|
item.Job.ID = jobs[i].ID
|
||||||
|
build.Jobs = append(build.Jobs, item.Job)
|
||||||
|
store.UpdateJob(c, item.Job)
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// publish topic
|
||||||
|
//
|
||||||
|
message := pubsub.Message{
|
||||||
|
Labels: map[string]string{
|
||||||
|
"repo": repo.FullName,
|
||||||
|
"private": strconv.FormatBool(repo.IsPrivate),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
message.Data, _ = json.Marshal(model.Event{
|
||||||
|
Type: model.Enqueued,
|
||||||
|
Repo: *repo,
|
||||||
|
Build: *build,
|
||||||
|
})
|
||||||
|
// TODO remove global reference
|
||||||
|
config.pubsub.Publish(c, "topic/events", message)
|
||||||
|
//
|
||||||
|
// end publish topic
|
||||||
|
//
|
||||||
|
|
||||||
|
for _, item := range items {
|
||||||
|
task := new(queue.Task)
|
||||||
|
task.ID = fmt.Sprint(item.Job.ID)
|
||||||
|
task.Labels = map[string]string{}
|
||||||
|
task.Labels["platform"] = item.Platform
|
||||||
|
for k, v := range item.Labels {
|
||||||
|
task.Labels[k] = v
|
||||||
|
}
|
||||||
|
|
||||||
|
task.Data, _ = json.Marshal(rpc.Pipeline{
|
||||||
|
ID: fmt.Sprint(item.Job.ID),
|
||||||
|
Config: item.Config,
|
||||||
|
Timeout: b.Repo.Timeout,
|
||||||
|
})
|
||||||
|
|
||||||
|
config.logger.Open(context.Background(), task.ID)
|
||||||
|
config.queue.Push(context.Background(), task)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package server
|
package debug
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http/pprof"
|
"net/http/pprof"
|
100
server/gitlab.go
100
server/gitlab.go
@ -1,100 +0,0 @@
|
|||||||
package server
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
|
|
||||||
"github.com/drone/drone/router/middleware/session"
|
|
||||||
"github.com/drone/drone/shared/token"
|
|
||||||
"github.com/drone/drone/store"
|
|
||||||
)
|
|
||||||
|
|
||||||
func GetCommit(c *gin.Context) {
|
|
||||||
repo := session.Repo(c)
|
|
||||||
|
|
||||||
parsed, err := token.ParseRequest(c.Request, func(t *token.Token) (string, error) {
|
|
||||||
return repo.Hash, nil
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
c.AbortWithError(http.StatusBadRequest, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if parsed.Text != repo.FullName {
|
|
||||||
c.AbortWithStatus(http.StatusUnauthorized)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
commit := c.Param("sha")
|
|
||||||
branch := c.Query("branch")
|
|
||||||
if len(branch) == 0 {
|
|
||||||
branch = repo.Branch
|
|
||||||
}
|
|
||||||
|
|
||||||
build, err := store.GetBuildCommit(c, repo, commit, branch)
|
|
||||||
if err != nil {
|
|
||||||
c.AbortWithError(http.StatusNotFound, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
c.JSON(http.StatusOK, build)
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetPullRequest(c *gin.Context) {
|
|
||||||
repo := session.Repo(c)
|
|
||||||
refs := fmt.Sprintf("refs/pull/%s/head", c.Param("number"))
|
|
||||||
|
|
||||||
parsed, err := token.ParseRequest(c.Request, func(t *token.Token) (string, error) {
|
|
||||||
return repo.Hash, nil
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
c.AbortWithError(http.StatusBadRequest, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if parsed.Text != repo.FullName {
|
|
||||||
c.AbortWithStatus(http.StatusUnauthorized)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
build, err := store.GetBuildRef(c, repo, refs)
|
|
||||||
if err != nil {
|
|
||||||
c.AbortWithError(http.StatusNotFound, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
c.JSON(http.StatusOK, build)
|
|
||||||
}
|
|
||||||
|
|
||||||
func RedirectSha(c *gin.Context) {
|
|
||||||
repo := session.Repo(c)
|
|
||||||
|
|
||||||
commit := c.Param("sha")
|
|
||||||
branch := c.Query("branch")
|
|
||||||
if len(branch) == 0 {
|
|
||||||
branch = repo.Branch
|
|
||||||
}
|
|
||||||
|
|
||||||
build, err := store.GetBuildCommit(c, repo, commit, branch)
|
|
||||||
if err != nil {
|
|
||||||
c.AbortWithError(http.StatusNotFound, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
path := fmt.Sprintf("/%s/%s/%d", repo.Owner, repo.Name, build.Number)
|
|
||||||
c.Redirect(http.StatusSeeOther, path)
|
|
||||||
}
|
|
||||||
|
|
||||||
func RedirectPullRequest(c *gin.Context) {
|
|
||||||
repo := session.Repo(c)
|
|
||||||
refs := fmt.Sprintf("refs/pull/%s/head", c.Param("number"))
|
|
||||||
|
|
||||||
build, err := store.GetBuildRef(c, repo, refs)
|
|
||||||
if err != nil {
|
|
||||||
c.AbortWithError(http.StatusNotFound, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
path := fmt.Sprintf("/%s/%s/%d", repo.Owner, repo.Name, build.Number)
|
|
||||||
c.Redirect(http.StatusSeeOther, path)
|
|
||||||
}
|
|
@ -2,7 +2,6 @@ package server
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"regexp"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
@ -18,8 +17,6 @@ import (
|
|||||||
"github.com/drone/mq/stomp"
|
"github.com/drone/mq/stomp"
|
||||||
)
|
)
|
||||||
|
|
||||||
var skipRe = regexp.MustCompile(`\[(?i:ci *skip|skip *ci)\]`)
|
|
||||||
|
|
||||||
func PostHook(c *gin.Context) {
|
func PostHook(c *gin.Context) {
|
||||||
remote_ := remote.FromContext(c)
|
remote_ := remote.FromContext(c)
|
||||||
|
|
||||||
@ -105,8 +102,8 @@ func PostHook(c *gin.Context) {
|
|||||||
// a small number of people will probably be upset by this, I'm not sure
|
// a small number of people will probably be upset by this, I'm not sure
|
||||||
// it is actually that big of a deal.
|
// it is actually that big of a deal.
|
||||||
if len(build.Email) == 0 {
|
if len(build.Email) == 0 {
|
||||||
author, err := store.GetUserLogin(c, build.Author)
|
author, uerr := store.GetUserLogin(c, build.Author)
|
||||||
if err == nil {
|
if uerr == nil {
|
||||||
build.Email = author.Email
|
build.Email = author.Email
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -164,9 +161,9 @@ func PostHook(c *gin.Context) {
|
|||||||
log.Debugf("cannot parse .drone.yml.sig file. empty file")
|
log.Debugf("cannot parse .drone.yml.sig file. empty file")
|
||||||
} else {
|
} else {
|
||||||
build.Signed = true
|
build.Signed = true
|
||||||
output, err := signature.Verify([]byte(repo.Hash))
|
output, verr := signature.Verify([]byte(repo.Hash))
|
||||||
if err != nil {
|
if verr != nil {
|
||||||
log.Debugf("cannot verify .drone.yml.sig file. %s", err)
|
log.Debugf("cannot verify .drone.yml.sig file. %s", verr)
|
||||||
} else if string(output) != string(raw) {
|
} else if string(output) != string(raw) {
|
||||||
log.Debugf("cannot verify .drone.yml.sig file. no match")
|
log.Debugf("cannot verify .drone.yml.sig file. no match")
|
||||||
} else {
|
} else {
|
||||||
@ -212,7 +209,7 @@ func PostHook(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
client := stomp.MustFromContext(c)
|
client := stomp.MustFromContext(c)
|
||||||
client.SendJSON("/topic/events", model.Event{
|
client.SendJSON("topic/events", model.Event{
|
||||||
Type: model.Enqueued,
|
Type: model.Enqueued,
|
||||||
Repo: *repo,
|
Repo: *repo,
|
||||||
Build: *build,
|
Build: *build,
|
||||||
@ -245,5 +242,4 @@ func PostHook(c *gin.Context) {
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
869
server/hook2.go
Normal file
869
server/hook2.go
Normal file
@ -0,0 +1,869 @@
|
|||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/square/go-jose"
|
||||||
|
|
||||||
|
"github.com/Sirupsen/logrus"
|
||||||
|
"github.com/drone/drone/model"
|
||||||
|
"github.com/drone/drone/remote"
|
||||||
|
"github.com/drone/drone/shared/httputil"
|
||||||
|
"github.com/drone/drone/shared/token"
|
||||||
|
"github.com/drone/drone/store"
|
||||||
|
"github.com/drone/envsubst"
|
||||||
|
|
||||||
|
"github.com/cncd/pipeline/pipeline/backend"
|
||||||
|
"github.com/cncd/pipeline/pipeline/frontend"
|
||||||
|
"github.com/cncd/pipeline/pipeline/frontend/yaml"
|
||||||
|
"github.com/cncd/pipeline/pipeline/frontend/yaml/compiler"
|
||||||
|
"github.com/cncd/pipeline/pipeline/frontend/yaml/linter"
|
||||||
|
"github.com/cncd/pipeline/pipeline/frontend/yaml/matrix"
|
||||||
|
"github.com/cncd/pipeline/pipeline/rpc"
|
||||||
|
"github.com/cncd/pubsub"
|
||||||
|
"github.com/cncd/queue"
|
||||||
|
)
|
||||||
|
|
||||||
|
//
|
||||||
|
// CANARY IMPLEMENTATION
|
||||||
|
//
|
||||||
|
// This file is a complete disaster because I'm trying to wedge in some
|
||||||
|
// experimental code. Please pardon our appearance during renovations.
|
||||||
|
//
|
||||||
|
|
||||||
|
var skipRe = regexp.MustCompile(`\[(?i:ci *skip|skip *ci)\]`)
|
||||||
|
|
||||||
|
func GetQueueInfo(c *gin.Context) {
|
||||||
|
c.IndentedJSON(200,
|
||||||
|
config.queue.Info(c),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// func PostHookOld(c *gin.Context) {
|
||||||
|
// remote_ := remote.FromContext(c)
|
||||||
|
//
|
||||||
|
// tmprepo, build, err := remote_.Hook(c.Request)
|
||||||
|
// if err != nil {
|
||||||
|
// logrus.Errorf("failure to parse hook. %s", err)
|
||||||
|
// c.AbortWithError(400, err)
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
// if build == nil {
|
||||||
|
// c.Writer.WriteHeader(200)
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
// if tmprepo == nil {
|
||||||
|
// logrus.Errorf("failure to ascertain repo from hook.")
|
||||||
|
// c.Writer.WriteHeader(400)
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // skip the build if any case-insensitive combination of the words "skip" and "ci"
|
||||||
|
// // wrapped in square brackets appear in the commit message
|
||||||
|
// skipMatch := skipRe.FindString(build.Message)
|
||||||
|
// if len(skipMatch) > 0 {
|
||||||
|
// logrus.Infof("ignoring hook. %s found in %s", skipMatch, build.Commit)
|
||||||
|
// c.Writer.WriteHeader(204)
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// repo, err := store.GetRepoOwnerName(c, tmprepo.Owner, tmprepo.Name)
|
||||||
|
// if err != nil {
|
||||||
|
// logrus.Errorf("failure to find repo %s/%s from hook. %s", tmprepo.Owner, tmprepo.Name, err)
|
||||||
|
// c.AbortWithError(404, err)
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // get the token and verify the hook is authorized
|
||||||
|
// parsed, err := token.ParseRequest(c.Request, func(t *token.Token) (string, error) {
|
||||||
|
// return repo.Hash, nil
|
||||||
|
// })
|
||||||
|
// if err != nil {
|
||||||
|
// logrus.Errorf("failure to parse token from hook for %s. %s", repo.FullName, err)
|
||||||
|
// c.AbortWithError(400, err)
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
// if parsed.Text != repo.FullName {
|
||||||
|
// logrus.Errorf("failure to verify token from hook. Expected %s, got %s", repo.FullName, parsed.Text)
|
||||||
|
// c.AbortWithStatus(403)
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if repo.UserID == 0 {
|
||||||
|
// logrus.Warnf("ignoring hook. repo %s has no owner.", repo.FullName)
|
||||||
|
// c.Writer.WriteHeader(204)
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
// var skipped = true
|
||||||
|
// if (build.Event == model.EventPush && repo.AllowPush) ||
|
||||||
|
// (build.Event == model.EventPull && repo.AllowPull) ||
|
||||||
|
// (build.Event == model.EventDeploy && repo.AllowDeploy) ||
|
||||||
|
// (build.Event == model.EventTag && repo.AllowTag) {
|
||||||
|
// skipped = false
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if skipped {
|
||||||
|
// logrus.Infof("ignoring hook. repo %s is disabled for %s events.", repo.FullName, build.Event)
|
||||||
|
// c.Writer.WriteHeader(204)
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// user, err := store.GetUser(c, repo.UserID)
|
||||||
|
// if err != nil {
|
||||||
|
// logrus.Errorf("failure to find repo owner %s. %s", repo.FullName, err)
|
||||||
|
// c.AbortWithError(500, err)
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // if the remote has a refresh token, the current access token
|
||||||
|
// // may be stale. Therefore, we should refresh prior to dispatching
|
||||||
|
// // the job.
|
||||||
|
// if refresher, ok := remote_.(remote.Refresher); ok {
|
||||||
|
// ok, _ := refresher.Refresh(user)
|
||||||
|
// if ok {
|
||||||
|
// store.UpdateUser(c, user)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // fetch the build file from the database
|
||||||
|
// cfg := ToConfig(c)
|
||||||
|
// raw, err := remote_.File(user, repo, build, cfg.Yaml)
|
||||||
|
// if err != nil {
|
||||||
|
// logrus.Errorf("failure to get build config for %s. %s", repo.FullName, err)
|
||||||
|
// c.AbortWithError(404, err)
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
// sec, err := remote_.File(user, repo, build, cfg.Shasum)
|
||||||
|
// if err != nil {
|
||||||
|
// logrus.Debugf("cannot find yaml signature for %s. %s", repo.FullName, err)
|
||||||
|
// // NOTE we don't exit on failure. The sec file is optional
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// axes, err := matrix.Parse(raw)
|
||||||
|
// if err != nil {
|
||||||
|
// c.String(500, "Failed to parse yaml file or calculate matrix. %s", err)
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
// if len(axes) == 0 {
|
||||||
|
// axes = append(axes, matrix.Axis{})
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// netrc, err := remote_.Netrc(user, repo)
|
||||||
|
// if err != nil {
|
||||||
|
// c.String(500, "Failed to generate netrc file. %s", err)
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // verify the branches can be built vs skipped
|
||||||
|
// branches, err := yaml.ParseBytes(raw)
|
||||||
|
// if err != nil {
|
||||||
|
// c.String(500, "Failed to parse yaml file. %s", err)
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
// if !branches.Branches.Match(build.Branch) && build.Event != model.EventTag && build.Event != model.EventDeploy {
|
||||||
|
// c.String(200, "Branch does not match restrictions defined in yaml")
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// signature, err := jose.ParseSigned(string(sec))
|
||||||
|
// if err != nil {
|
||||||
|
// logrus.Debugf("cannot parse .drone.yml.sig file. %s", err)
|
||||||
|
// } else if len(sec) == 0 {
|
||||||
|
// logrus.Debugf("cannot parse .drone.yml.sig file. empty file")
|
||||||
|
// } else {
|
||||||
|
// build.Signed = true
|
||||||
|
// output, verr := signature.Verify([]byte(repo.Hash))
|
||||||
|
// if verr != nil {
|
||||||
|
// logrus.Debugf("cannot verify .drone.yml.sig file. %s", verr)
|
||||||
|
// } else if string(output) != string(raw) {
|
||||||
|
// logrus.Debugf("cannot verify .drone.yml.sig file. no match")
|
||||||
|
// } else {
|
||||||
|
// build.Verified = true
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // update some build fields
|
||||||
|
// build.Status = model.StatusPending
|
||||||
|
// build.RepoID = repo.ID
|
||||||
|
//
|
||||||
|
// // and use a transaction
|
||||||
|
// var jobs []*model.Job
|
||||||
|
// for num, axis := range axes {
|
||||||
|
// jobs = append(jobs, &model.Job{
|
||||||
|
// BuildID: build.ID,
|
||||||
|
// Number: num + 1,
|
||||||
|
// Status: model.StatusPending,
|
||||||
|
// Environment: axis,
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
// err = store.CreateBuild(c, build, jobs...)
|
||||||
|
// if err != nil {
|
||||||
|
// logrus.Errorf("failure to save commit for %s. %s", repo.FullName, err)
|
||||||
|
// c.AbortWithError(500, err)
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// c.JSON(200, build)
|
||||||
|
//
|
||||||
|
// uri := fmt.Sprintf("%s/%s/%d", httputil.GetURL(c.Request), repo.FullName, build.Number)
|
||||||
|
// err = remote_.Status(user, repo, build, uri)
|
||||||
|
// if err != nil {
|
||||||
|
// logrus.Errorf("error setting commit status for %s/%d", repo.FullName, build.Number)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // get the previous build so that we can send
|
||||||
|
// // on status change notifications
|
||||||
|
// last, _ := store.GetBuildLastBefore(c, repo, build.Branch, build.ID)
|
||||||
|
// secs, err := store.GetMergedSecretList(c, repo)
|
||||||
|
// if err != nil {
|
||||||
|
// logrus.Debugf("Error getting secrets for %s#%d. %s", repo.FullName, build.Number, err)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// //
|
||||||
|
// // BELOW: NEW
|
||||||
|
// //
|
||||||
|
//
|
||||||
|
// b := builder{
|
||||||
|
// Repo: repo,
|
||||||
|
// Curr: build,
|
||||||
|
// Last: last,
|
||||||
|
// Netrc: netrc,
|
||||||
|
// Secs: secs,
|
||||||
|
// Link: httputil.GetURL(c.Request),
|
||||||
|
// Yaml: string(raw),
|
||||||
|
// }
|
||||||
|
// items, err := b.Build()
|
||||||
|
// if err != nil {
|
||||||
|
// build.Status = model.StatusError
|
||||||
|
// build.Started = time.Now().Unix()
|
||||||
|
// build.Finished = build.Started
|
||||||
|
// build.Error = err.Error()
|
||||||
|
// store.CreateBuild(c, build, build.Jobs...)
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// for _, item := range items {
|
||||||
|
// build.Jobs = append(build.Jobs, item.Job)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if err := store.CreateBuild(c, build, build.Jobs...); err != nil {
|
||||||
|
// logrus.Errorf("failure to save commit for %s. %s", repo.FullName, err)
|
||||||
|
// c.AbortWithError(500, err)
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// for _, item := range items {
|
||||||
|
//
|
||||||
|
// task := new(queue.Task)
|
||||||
|
// task.ID = fmt.Sprint(item.Job.ID)
|
||||||
|
// task.Labels = map[string]string{}
|
||||||
|
// task.Labels["platform"] = item.Platform
|
||||||
|
// for k, v := range item.Labels {
|
||||||
|
// task.Labels[k] = v
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// task.Data, _ = json.Marshal(rpc.Pipeline{
|
||||||
|
// ID: fmt.Sprint(item.Job.ID),
|
||||||
|
// Config: item.Config,
|
||||||
|
// Timeout: b.Repo.Timeout,
|
||||||
|
// })
|
||||||
|
//
|
||||||
|
// config.logger.Open(context.Background(), task.ID)
|
||||||
|
// config.queue.Push(context.Background(), task)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// //
|
||||||
|
// // new code here
|
||||||
|
// //
|
||||||
|
//
|
||||||
|
// message := pubsub.Message{
|
||||||
|
// Labels: map[string]string{
|
||||||
|
// "repo": repo.FullName,
|
||||||
|
// "private": strconv.FormatBool(repo.IsPrivate),
|
||||||
|
// },
|
||||||
|
// }
|
||||||
|
// message.Data, _ = json.Marshal(model.Event{
|
||||||
|
// Type: model.Enqueued,
|
||||||
|
// Repo: *repo,
|
||||||
|
// Build: *build,
|
||||||
|
// })
|
||||||
|
// // TODO remove global reference
|
||||||
|
// config.pubsub.Publish(c, "topic/events", message)
|
||||||
|
//
|
||||||
|
// //
|
||||||
|
// // workspace
|
||||||
|
// //
|
||||||
|
//
|
||||||
|
// for _, job := range jobs {
|
||||||
|
//
|
||||||
|
// metadata := metadataFromStruct(repo, build, last, job, httputil.GetURL(c.Request))
|
||||||
|
// environ := metadata.Environ()
|
||||||
|
//
|
||||||
|
// secrets := map[string]string{}
|
||||||
|
// for _, sec := range secs {
|
||||||
|
// if !sec.MatchEvent(build.Event) {
|
||||||
|
// continue
|
||||||
|
// }
|
||||||
|
// if build.Verified || sec.SkipVerify {
|
||||||
|
// secrets[sec.Name] = sec.Value
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// sub := func(name string) string {
|
||||||
|
// if v, ok := environ[name]; ok {
|
||||||
|
// return v
|
||||||
|
// }
|
||||||
|
// return secrets[name]
|
||||||
|
// }
|
||||||
|
// if s, err := envsubst.Eval(string(raw), sub); err != nil {
|
||||||
|
// raw = []byte(s)
|
||||||
|
// }
|
||||||
|
// parsed, err := yaml.ParseBytes(raw)
|
||||||
|
// if err != nil {
|
||||||
|
// job.ExitCode = 255
|
||||||
|
// job.Enqueued = time.Now().Unix()
|
||||||
|
// job.Started = time.Now().Unix()
|
||||||
|
// job.Finished = time.Now().Unix()
|
||||||
|
// job.Error = err.Error()
|
||||||
|
// store.UpdateBuildJob(c, build, job)
|
||||||
|
// continue
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// lerr := linter.New(
|
||||||
|
// linter.WithTrusted(repo.IsTrusted),
|
||||||
|
// ).Lint(parsed)
|
||||||
|
// if lerr != nil {
|
||||||
|
// job.ExitCode = 255
|
||||||
|
// job.Enqueued = time.Now().Unix()
|
||||||
|
// job.Started = time.Now().Unix()
|
||||||
|
// job.Finished = time.Now().Unix()
|
||||||
|
// job.Error = lerr.Error()
|
||||||
|
// store.UpdateBuildJob(c, build, job)
|
||||||
|
// continue
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// ir := compiler.New(
|
||||||
|
// compiler.WithEnviron(environ),
|
||||||
|
// // TODO ability to customize the escalated plugins
|
||||||
|
// compiler.WithEscalated("plugins/docker", "plugins/gcr", "plugins/ecr"),
|
||||||
|
// compiler.WithLocal(false),
|
||||||
|
// compiler.WithNetrc(netrc.Login, netrc.Password, netrc.Machine),
|
||||||
|
// compiler.WithPrefix(
|
||||||
|
// fmt.Sprintf(
|
||||||
|
// "%d_%d",
|
||||||
|
// job.ID,
|
||||||
|
// time.Now().Unix(),
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// compiler.WithEnviron(job.Environment),
|
||||||
|
// compiler.WithProxy(),
|
||||||
|
// // TODO ability to set global volumes for things like certs
|
||||||
|
// compiler.WithVolumes(),
|
||||||
|
// compiler.WithWorkspaceFromURL("/drone", repo.Link),
|
||||||
|
// ).Compile(parsed)
|
||||||
|
//
|
||||||
|
// // TODO there is a chicken and egg problem here because
|
||||||
|
// // the compiled yaml has a platform environment variable
|
||||||
|
// // that is not correctly set, because we are just about
|
||||||
|
// // to set it ....
|
||||||
|
// // TODO maybe we remove platform from metadata and let
|
||||||
|
// // the compiler set the value from the yaml itself.
|
||||||
|
// if parsed.Platform == "" {
|
||||||
|
// parsed.Platform = "linux/amd64"
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// for _, sec := range secs {
|
||||||
|
// if !sec.MatchEvent(build.Event) {
|
||||||
|
// continue
|
||||||
|
// }
|
||||||
|
// if build.Verified || sec.SkipVerify {
|
||||||
|
// ir.Secrets = append(ir.Secrets, &backend.Secret{
|
||||||
|
// Mask: sec.Conceal,
|
||||||
|
// Name: sec.Name,
|
||||||
|
// Value: sec.Value,
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// task := new(queue.Task)
|
||||||
|
// task.ID = fmt.Sprint(job.ID)
|
||||||
|
// task.Labels = map[string]string{}
|
||||||
|
// task.Labels["platform"] = parsed.Platform
|
||||||
|
// if parsed.Labels != nil {
|
||||||
|
// for k, v := range parsed.Labels {
|
||||||
|
// task.Labels[k] = v
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// task.Data, _ = json.Marshal(rpc.Pipeline{
|
||||||
|
// ID: fmt.Sprint(job.ID),
|
||||||
|
// Config: ir,
|
||||||
|
// Timeout: repo.Timeout,
|
||||||
|
// })
|
||||||
|
//
|
||||||
|
// config.logger.Open(context.Background(), task.ID)
|
||||||
|
// config.queue.Push(context.Background(), task)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
|
||||||
|
// return the metadata from the cli context.
|
||||||
|
func metadataFromStruct(repo *model.Repo, build, last *model.Build, job *model.Job, link string) frontend.Metadata {
|
||||||
|
return frontend.Metadata{
|
||||||
|
Repo: frontend.Repo{
|
||||||
|
Name: repo.Name,
|
||||||
|
Link: repo.Link,
|
||||||
|
Remote: repo.Clone,
|
||||||
|
Private: repo.IsPrivate,
|
||||||
|
},
|
||||||
|
Curr: frontend.Build{
|
||||||
|
Number: build.Number,
|
||||||
|
Created: build.Created,
|
||||||
|
Started: build.Started,
|
||||||
|
Finished: build.Finished,
|
||||||
|
Status: build.Status,
|
||||||
|
Event: build.Event,
|
||||||
|
Link: build.Link,
|
||||||
|
Target: build.Deploy,
|
||||||
|
Commit: frontend.Commit{
|
||||||
|
Sha: build.Commit,
|
||||||
|
Ref: build.Ref,
|
||||||
|
Refspec: build.Refspec,
|
||||||
|
Branch: build.Branch,
|
||||||
|
Message: build.Message,
|
||||||
|
Author: frontend.Author{
|
||||||
|
Name: build.Author,
|
||||||
|
Email: build.Email,
|
||||||
|
Avatar: build.Avatar,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Prev: frontend.Build{
|
||||||
|
Number: last.Number,
|
||||||
|
Created: last.Created,
|
||||||
|
Started: last.Started,
|
||||||
|
Finished: last.Finished,
|
||||||
|
Status: last.Status,
|
||||||
|
Event: last.Event,
|
||||||
|
Link: last.Link,
|
||||||
|
Target: last.Deploy,
|
||||||
|
Commit: frontend.Commit{
|
||||||
|
Sha: last.Commit,
|
||||||
|
Ref: last.Ref,
|
||||||
|
Refspec: last.Refspec,
|
||||||
|
Branch: last.Branch,
|
||||||
|
Message: last.Message,
|
||||||
|
Author: frontend.Author{
|
||||||
|
Name: last.Author,
|
||||||
|
Email: last.Email,
|
||||||
|
Avatar: last.Avatar,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Job: frontend.Job{
|
||||||
|
Number: job.Number,
|
||||||
|
Matrix: job.Environment,
|
||||||
|
},
|
||||||
|
Sys: frontend.System{
|
||||||
|
Name: "drone",
|
||||||
|
Link: link,
|
||||||
|
Arch: "linux/amd64",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// use helper funciton to return ([]backend.Config, error)
|
||||||
|
|
||||||
|
// 1. fetch everything from github
|
||||||
|
// 2. create and persist the build object
|
||||||
|
//
|
||||||
|
// 3. generate the build jobs [Launcher?]
|
||||||
|
// a. parse yaml
|
||||||
|
// b. lint yaml
|
||||||
|
// c. compile yaml
|
||||||
|
//
|
||||||
|
// 4. persist the build jobs (... what if I already have jobs, via re-start)
|
||||||
|
// 5. update github status
|
||||||
|
// 6. send to queue
|
||||||
|
// 7. trigger pubsub
|
||||||
|
|
||||||
|
type builder struct {
|
||||||
|
Repo *model.Repo
|
||||||
|
Curr *model.Build
|
||||||
|
Last *model.Build
|
||||||
|
Netrc *model.Netrc
|
||||||
|
Secs []*model.Secret
|
||||||
|
Link string
|
||||||
|
Yaml string
|
||||||
|
}
|
||||||
|
|
||||||
|
type buildItem struct {
|
||||||
|
Job *model.Job
|
||||||
|
Platform string
|
||||||
|
Labels map[string]string
|
||||||
|
Config *backend.Config
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *builder) Build() ([]*buildItem, error) {
|
||||||
|
|
||||||
|
axes, err := matrix.ParseString(b.Yaml)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(axes) == 0 {
|
||||||
|
axes = append(axes, matrix.Axis{})
|
||||||
|
}
|
||||||
|
|
||||||
|
var items []*buildItem
|
||||||
|
for i, axis := range axes {
|
||||||
|
job := &model.Job{
|
||||||
|
BuildID: b.Curr.ID,
|
||||||
|
Number: i + 1,
|
||||||
|
Status: model.StatusPending,
|
||||||
|
Environment: axis,
|
||||||
|
Enqueued: b.Curr.Created,
|
||||||
|
}
|
||||||
|
|
||||||
|
metadata := metadataFromStruct(b.Repo, b.Curr, b.Last, job, b.Link)
|
||||||
|
environ := metadata.Environ()
|
||||||
|
for k, v := range metadata.EnvironDrone() {
|
||||||
|
environ[k] = v
|
||||||
|
}
|
||||||
|
|
||||||
|
secrets := map[string]string{}
|
||||||
|
for _, sec := range b.Secs {
|
||||||
|
if !sec.MatchEvent(b.Curr.Event) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if b.Curr.Verified || sec.SkipVerify {
|
||||||
|
secrets[sec.Name] = sec.Value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sub := func(name string) string {
|
||||||
|
if v, ok := environ[name]; ok {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
return secrets[name]
|
||||||
|
}
|
||||||
|
|
||||||
|
y := b.Yaml
|
||||||
|
if s, err := envsubst.Eval(y, sub); err != nil {
|
||||||
|
y = s
|
||||||
|
}
|
||||||
|
|
||||||
|
parsed, err := yaml.ParseString(y)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
metadata.Sys.Arch = parsed.Platform
|
||||||
|
if metadata.Sys.Arch == "" {
|
||||||
|
metadata.Sys.Arch = "linux/amd64"
|
||||||
|
}
|
||||||
|
|
||||||
|
lerr := linter.New(
|
||||||
|
linter.WithTrusted(b.Repo.IsTrusted),
|
||||||
|
).Lint(parsed)
|
||||||
|
if lerr != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ir := compiler.New(
|
||||||
|
compiler.WithEnviron(environ),
|
||||||
|
// TODO ability to customize the escalated plugins
|
||||||
|
compiler.WithEscalated("plugins/docker", "plugins/gcr", "plugins/ecr"),
|
||||||
|
compiler.WithLocal(false),
|
||||||
|
compiler.WithNetrc(b.Netrc.Login, b.Netrc.Password, b.Netrc.Machine),
|
||||||
|
compiler.WithPrefix(
|
||||||
|
fmt.Sprintf(
|
||||||
|
"%d_%d",
|
||||||
|
job.ID,
|
||||||
|
time.Now().Unix(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
compiler.WithEnviron(job.Environment),
|
||||||
|
compiler.WithProxy(),
|
||||||
|
// TODO ability to set global volumes for things like certs
|
||||||
|
compiler.WithVolumes(),
|
||||||
|
compiler.WithWorkspaceFromURL("/drone", b.Curr.Link),
|
||||||
|
).Compile(parsed)
|
||||||
|
|
||||||
|
for _, sec := range b.Secs {
|
||||||
|
if !sec.MatchEvent(b.Curr.Event) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if b.Curr.Verified || sec.SkipVerify {
|
||||||
|
ir.Secrets = append(ir.Secrets, &backend.Secret{
|
||||||
|
Mask: sec.Conceal,
|
||||||
|
Name: sec.Name,
|
||||||
|
Value: sec.Value,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
item := &buildItem{
|
||||||
|
Job: job,
|
||||||
|
Config: ir,
|
||||||
|
Labels: parsed.Labels,
|
||||||
|
Platform: metadata.Sys.Arch,
|
||||||
|
}
|
||||||
|
if item.Labels == nil {
|
||||||
|
item.Labels = map[string]string{}
|
||||||
|
}
|
||||||
|
items = append(items, item)
|
||||||
|
}
|
||||||
|
|
||||||
|
return items, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
|
||||||
|
func PostHook2(c *gin.Context) {
|
||||||
|
remote_ := remote.FromContext(c)
|
||||||
|
|
||||||
|
tmprepo, build, err := remote_.Hook(c.Request)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Errorf("failure to parse hook. %s", err)
|
||||||
|
c.AbortWithError(400, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if build == nil {
|
||||||
|
c.Writer.WriteHeader(200)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if tmprepo == nil {
|
||||||
|
logrus.Errorf("failure to ascertain repo from hook.")
|
||||||
|
c.Writer.WriteHeader(400)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// skip the build if any case-insensitive combination of the words "skip" and "ci"
|
||||||
|
// wrapped in square brackets appear in the commit message
|
||||||
|
skipMatch := skipRe.FindString(build.Message)
|
||||||
|
if len(skipMatch) > 0 {
|
||||||
|
logrus.Infof("ignoring hook. %s found in %s", skipMatch, build.Commit)
|
||||||
|
c.Writer.WriteHeader(204)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
repo, err := store.GetRepoOwnerName(c, tmprepo.Owner, tmprepo.Name)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Errorf("failure to find repo %s/%s from hook. %s", tmprepo.Owner, tmprepo.Name, err)
|
||||||
|
c.AbortWithError(404, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the token and verify the hook is authorized
|
||||||
|
parsed, err := token.ParseRequest(c.Request, func(t *token.Token) (string, error) {
|
||||||
|
return repo.Hash, nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
logrus.Errorf("failure to parse token from hook for %s. %s", repo.FullName, err)
|
||||||
|
c.AbortWithError(400, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if parsed.Text != repo.FullName {
|
||||||
|
logrus.Errorf("failure to verify token from hook. Expected %s, got %s", repo.FullName, parsed.Text)
|
||||||
|
c.AbortWithStatus(403)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if repo.UserID == 0 {
|
||||||
|
logrus.Warnf("ignoring hook. repo %s has no owner.", repo.FullName)
|
||||||
|
c.Writer.WriteHeader(204)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var skipped = true
|
||||||
|
if (build.Event == model.EventPush && repo.AllowPush) ||
|
||||||
|
(build.Event == model.EventPull && repo.AllowPull) ||
|
||||||
|
(build.Event == model.EventDeploy && repo.AllowDeploy) ||
|
||||||
|
(build.Event == model.EventTag && repo.AllowTag) {
|
||||||
|
skipped = false
|
||||||
|
}
|
||||||
|
|
||||||
|
if skipped {
|
||||||
|
logrus.Infof("ignoring hook. repo %s is disabled for %s events.", repo.FullName, build.Event)
|
||||||
|
c.Writer.WriteHeader(204)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
user, err := store.GetUser(c, repo.UserID)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Errorf("failure to find repo owner %s. %s", repo.FullName, err)
|
||||||
|
c.AbortWithError(500, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the remote has a refresh token, the current access token
|
||||||
|
// may be stale. Therefore, we should refresh prior to dispatching
|
||||||
|
// the job.
|
||||||
|
if refresher, ok := remote_.(remote.Refresher); ok {
|
||||||
|
ok, _ := refresher.Refresh(user)
|
||||||
|
if ok {
|
||||||
|
store.UpdateUser(c, user)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fetch the build file from the database
|
||||||
|
cfg := ToConfig(c)
|
||||||
|
raw, err := remote_.File(user, repo, build, cfg.Yaml)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Errorf("failure to get build config for %s. %s", repo.FullName, err)
|
||||||
|
c.AbortWithError(404, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
sec, err := remote_.File(user, repo, build, cfg.Shasum)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Debugf("cannot find yaml signature for %s. %s", repo.FullName, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
netrc, err := remote_.Netrc(user, repo)
|
||||||
|
if err != nil {
|
||||||
|
c.String(500, "Failed to generate netrc file. %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// verify the branches can be built vs skipped
|
||||||
|
branches, err := yaml.ParseBytes(raw)
|
||||||
|
if err != nil {
|
||||||
|
c.String(500, "Failed to parse yaml file. %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !branches.Branches.Match(build.Branch) && build.Event != model.EventTag && build.Event != model.EventDeploy {
|
||||||
|
c.String(200, "Branch does not match restrictions defined in yaml")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
signature, err := jose.ParseSigned(string(sec))
|
||||||
|
if err != nil {
|
||||||
|
logrus.Debugf("cannot parse .drone.yml.sig file. %s", err)
|
||||||
|
} else if len(sec) == 0 {
|
||||||
|
logrus.Debugf("cannot parse .drone.yml.sig file. empty file")
|
||||||
|
} else {
|
||||||
|
build.Signed = true
|
||||||
|
output, verr := signature.Verify([]byte(repo.Hash))
|
||||||
|
if verr != nil {
|
||||||
|
logrus.Debugf("cannot verify .drone.yml.sig file. %s", verr)
|
||||||
|
} else if string(output) != string(raw) {
|
||||||
|
logrus.Debugf("cannot verify .drone.yml.sig file. no match")
|
||||||
|
} else {
|
||||||
|
build.Verified = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// update some build fields
|
||||||
|
build.Status = model.StatusPending
|
||||||
|
build.RepoID = repo.ID
|
||||||
|
|
||||||
|
if err := store.CreateBuild(c, build, build.Jobs...); err != nil {
|
||||||
|
logrus.Errorf("failure to save commit for %s. %s", repo.FullName, err)
|
||||||
|
c.AbortWithError(500, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(200, build)
|
||||||
|
|
||||||
|
// get the previous build so that we can send
|
||||||
|
// on status change notifications
|
||||||
|
last, _ := store.GetBuildLastBefore(c, repo, build.Branch, build.ID)
|
||||||
|
secs, err := store.GetMergedSecretList(c, repo)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Debugf("Error getting secrets for %s#%d. %s", repo.FullName, build.Number, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// BELOW: NEW
|
||||||
|
//
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
uri := fmt.Sprintf("%s/%s/%d", httputil.GetURL(c.Request), repo.FullName, build.Number)
|
||||||
|
err = remote_.Status(user, repo, build, uri)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Errorf("error setting commit status for %s/%d", repo.FullName, build.Number)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
b := builder{
|
||||||
|
Repo: repo,
|
||||||
|
Curr: build,
|
||||||
|
Last: last,
|
||||||
|
Netrc: netrc,
|
||||||
|
Secs: secs,
|
||||||
|
Link: httputil.GetURL(c.Request),
|
||||||
|
Yaml: string(raw),
|
||||||
|
}
|
||||||
|
items, err := b.Build()
|
||||||
|
if err != nil {
|
||||||
|
build.Status = model.StatusError
|
||||||
|
build.Started = time.Now().Unix()
|
||||||
|
build.Finished = build.Started
|
||||||
|
build.Error = err.Error()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, item := range items {
|
||||||
|
build.Jobs = append(build.Jobs, item.Job)
|
||||||
|
store.CreateJob(c, item.Job)
|
||||||
|
// TODO err
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// publish topic
|
||||||
|
//
|
||||||
|
message := pubsub.Message{
|
||||||
|
Labels: map[string]string{
|
||||||
|
"repo": repo.FullName,
|
||||||
|
"private": strconv.FormatBool(repo.IsPrivate),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
message.Data, _ = json.Marshal(model.Event{
|
||||||
|
Type: model.Enqueued,
|
||||||
|
Repo: *repo,
|
||||||
|
Build: *build,
|
||||||
|
})
|
||||||
|
// TODO remove global reference
|
||||||
|
config.pubsub.Publish(c, "topic/events", message)
|
||||||
|
//
|
||||||
|
// end publish topic
|
||||||
|
//
|
||||||
|
|
||||||
|
for _, item := range items {
|
||||||
|
task := new(queue.Task)
|
||||||
|
task.ID = fmt.Sprint(item.Job.ID)
|
||||||
|
task.Labels = map[string]string{}
|
||||||
|
task.Labels["platform"] = item.Platform
|
||||||
|
for k, v := range item.Labels {
|
||||||
|
task.Labels[k] = v
|
||||||
|
}
|
||||||
|
|
||||||
|
task.Data, _ = json.Marshal(rpc.Pipeline{
|
||||||
|
ID: fmt.Sprint(item.Job.ID),
|
||||||
|
Config: item.Config,
|
||||||
|
Timeout: b.Repo.Timeout,
|
||||||
|
})
|
||||||
|
|
||||||
|
config.logger.Open(context.Background(), task.ID)
|
||||||
|
config.queue.Push(context.Background(), task)
|
||||||
|
}
|
||||||
|
}
|
228
server/rpc.go
Normal file
228
server/rpc.go
Normal file
@ -0,0 +1,228 @@
|
|||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/Sirupsen/logrus"
|
||||||
|
"github.com/cncd/logging"
|
||||||
|
"github.com/cncd/pipeline/pipeline/rpc"
|
||||||
|
"github.com/cncd/pubsub"
|
||||||
|
"github.com/cncd/queue"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
|
||||||
|
"github.com/drone/drone/model"
|
||||||
|
"github.com/drone/drone/remote"
|
||||||
|
"github.com/drone/drone/store"
|
||||||
|
)
|
||||||
|
|
||||||
|
// This file is a complete disaster because I'm trying to wedge in some
|
||||||
|
// experimental code. Please pardon our appearance during renovations.
|
||||||
|
|
||||||
|
var config = struct {
|
||||||
|
pubsub pubsub.Publisher
|
||||||
|
queue queue.Queue
|
||||||
|
logger logging.Log
|
||||||
|
secret string
|
||||||
|
host string
|
||||||
|
}{
|
||||||
|
pubsub.New(),
|
||||||
|
queue.New(),
|
||||||
|
logging.New(),
|
||||||
|
os.Getenv("DRONE_SECRET"),
|
||||||
|
os.Getenv("DRONE_HOST"),
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
config.pubsub.Create(context.Background(), "topic/events")
|
||||||
|
}
|
||||||
|
|
||||||
|
// func SetupRPC() gin.HandlerFunc {
|
||||||
|
// return func(c *gin.Context) {
|
||||||
|
// c.Next()
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
func RPCHandler(c *gin.Context) {
|
||||||
|
|
||||||
|
if secret := c.Request.Header.Get("Authorization"); secret != "Bearer "+config.secret {
|
||||||
|
log.Printf("Unable to connect agent. Invalid authorization token %q does not match %q", secret, config.secret)
|
||||||
|
c.String(401, "Unable to connect agent. Invalid authorization token")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
peer := RPC{
|
||||||
|
remote: remote.FromContext(c),
|
||||||
|
store: store.FromContext(c),
|
||||||
|
queue: config.queue,
|
||||||
|
pubsub: config.pubsub,
|
||||||
|
logger: config.logger,
|
||||||
|
host: config.host,
|
||||||
|
}
|
||||||
|
rpc.NewServer(&peer).ServeHTTP(c.Writer, c.Request)
|
||||||
|
}
|
||||||
|
|
||||||
|
type RPC struct {
|
||||||
|
remote remote.Remote
|
||||||
|
queue queue.Queue
|
||||||
|
pubsub pubsub.Publisher
|
||||||
|
logger logging.Log
|
||||||
|
store store.Store
|
||||||
|
host string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next implements the rpc.Next function
|
||||||
|
func (s *RPC) Next(c context.Context, filter rpc.Filter) (*rpc.Pipeline, error) {
|
||||||
|
fn := func(task *queue.Task) bool {
|
||||||
|
for k, v := range filter.Labels {
|
||||||
|
if task.Labels[k] != v {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
task, err := s.queue.Poll(c, fn)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else if task == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
pipeline := new(rpc.Pipeline)
|
||||||
|
err = json.Unmarshal(task.Data, pipeline)
|
||||||
|
return pipeline, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait implements the rpc.Wait function
|
||||||
|
func (s *RPC) Wait(c context.Context, id string) error {
|
||||||
|
return s.queue.Wait(c, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extend implements the rpc.Extend function
|
||||||
|
func (s *RPC) Extend(c context.Context, id string) error {
|
||||||
|
return s.queue.Extend(c, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update implements the rpc.Update function
|
||||||
|
func (s *RPC) Update(c context.Context, id string, state rpc.State) error {
|
||||||
|
jobID, err := strconv.ParseInt(id, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
job, err := s.store.GetJob(jobID)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("error: cannot find job with id %d: %s", jobID, err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
build, err := s.store.GetBuild(job.BuildID)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("error: cannot find build with id %d: %s", job.BuildID, err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
repo, err := s.store.GetRepo(build.RepoID)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("error: cannot find repo with id %d: %s", build.RepoID, err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if build.Status != model.StatusRunning {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
job.Started = state.Started
|
||||||
|
job.Finished = state.Finished
|
||||||
|
job.ExitCode = state.ExitCode
|
||||||
|
job.Status = model.StatusRunning
|
||||||
|
job.Error = state.Error
|
||||||
|
|
||||||
|
if build.Status == model.StatusPending {
|
||||||
|
build.Started = job.Started
|
||||||
|
build.Status = model.StatusRunning
|
||||||
|
s.store.UpdateBuild(build)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("pipeline: update %s: exited=%v, exit_code=%d", id, state.Exited, state.ExitCode)
|
||||||
|
|
||||||
|
if state.Exited {
|
||||||
|
|
||||||
|
job.Status = model.StatusSuccess
|
||||||
|
if job.ExitCode != 0 || job.Error != "" {
|
||||||
|
job.Status = model.StatusFailure
|
||||||
|
}
|
||||||
|
|
||||||
|
// save the logs
|
||||||
|
var buf bytes.Buffer
|
||||||
|
if serr := s.logger.Snapshot(context.Background(), id, &buf); serr != nil {
|
||||||
|
log.Printf("error: snapshotting logs: %s", serr)
|
||||||
|
}
|
||||||
|
if werr := s.store.WriteLog(job, &buf); werr != nil {
|
||||||
|
log.Printf("error: persisting logs: %s", werr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// close the logger
|
||||||
|
s.logger.Close(c, id)
|
||||||
|
s.queue.Done(c, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
// hackity hack
|
||||||
|
cc := context.WithValue(c, "store", s.store)
|
||||||
|
ok, uerr := store.UpdateBuildJob(cc, build, job)
|
||||||
|
if uerr != nil {
|
||||||
|
log.Printf("error: updating job: %s", uerr)
|
||||||
|
}
|
||||||
|
if ok {
|
||||||
|
// get the user because we transfer the user form the server to agent
|
||||||
|
// and back we lose the token which does not get serialized to json.
|
||||||
|
user, uerr := s.store.GetUser(repo.UserID)
|
||||||
|
if uerr != nil {
|
||||||
|
logrus.Errorf("Unable to find user. %s", err)
|
||||||
|
} else {
|
||||||
|
s.remote.Status(user, repo, build,
|
||||||
|
fmt.Sprintf("%s/%s/%d", s.host, repo.FullName, build.Number))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message := pubsub.Message{}
|
||||||
|
message.Data, _ = json.Marshal(model.Event{
|
||||||
|
Type: func() model.EventType {
|
||||||
|
// HACK we don't even really care about the event type.
|
||||||
|
// so we should just simplify how events are triggered.
|
||||||
|
// WTF was this being used for?????????????????????????
|
||||||
|
if job.Status == model.StatusRunning {
|
||||||
|
return model.Started
|
||||||
|
}
|
||||||
|
return model.Finished
|
||||||
|
}(),
|
||||||
|
Repo: *repo,
|
||||||
|
Build: *build,
|
||||||
|
Job: *job,
|
||||||
|
})
|
||||||
|
message.Labels = map[string]string{
|
||||||
|
"repo": repo.FullName,
|
||||||
|
"private": strconv.FormatBool(repo.IsPrivate),
|
||||||
|
}
|
||||||
|
s.pubsub.Publish(c, "topic/events", message)
|
||||||
|
log.Println("finish rpc.update")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Upload implements the rpc.Upload function
|
||||||
|
func (s *RPC) Upload(c context.Context, id, mime string, file io.Reader) error { return nil }
|
||||||
|
|
||||||
|
// Done implements the rpc.Done function
|
||||||
|
func (s *RPC) Done(c context.Context, id string) error { return nil }
|
||||||
|
|
||||||
|
// Log implements the rpc.Log function
|
||||||
|
func (s *RPC) Log(c context.Context, id string, line *rpc.Line) error {
|
||||||
|
entry := new(logging.Entry)
|
||||||
|
entry.Data, _ = json.Marshal(line)
|
||||||
|
s.logger.Write(c, id, entry)
|
||||||
|
return nil
|
||||||
|
}
|
158
server/stream.go
158
server/stream.go
@ -1,10 +1,13 @@
|
|||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/cncd/logging"
|
||||||
|
"github.com/cncd/pubsub"
|
||||||
"github.com/drone/drone/cache"
|
"github.com/drone/drone/cache"
|
||||||
"github.com/drone/drone/model"
|
"github.com/drone/drone/model"
|
||||||
"github.com/drone/drone/router/middleware/session"
|
"github.com/drone/drone/router/middleware/session"
|
||||||
@ -194,3 +197,158 @@ func reader(ws *websocket.Conn) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// CANARY IMPLEMENTATION
|
||||||
|
//
|
||||||
|
// This file is a complete disaster because I'm trying to wedge in some
|
||||||
|
// experimental code. Please pardon our appearance during renovations.
|
||||||
|
//
|
||||||
|
|
||||||
|
func LogStream2(c *gin.Context) {
|
||||||
|
repo := session.Repo(c)
|
||||||
|
buildn, _ := strconv.Atoi(c.Param("build"))
|
||||||
|
jobn, _ := strconv.Atoi(c.Param("number"))
|
||||||
|
|
||||||
|
build, err := store.GetBuildNumber(c, repo, buildn)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Debugln("stream cannot get build number.", err)
|
||||||
|
c.AbortWithError(404, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
job, err := store.GetJobNumber(c, build, jobn)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Debugln("stream cannot get job number.", err)
|
||||||
|
c.AbortWithError(404, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if job.Status != model.StatusRunning {
|
||||||
|
logrus.Debugln("stream not found.")
|
||||||
|
c.AbortWithStatus(404)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ws, err := upgrader.Upgrade(c.Writer, c.Request, nil)
|
||||||
|
if err != nil {
|
||||||
|
if _, ok := err.(websocket.HandshakeError); !ok {
|
||||||
|
logrus.Errorf("Cannot upgrade websocket. %s", err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
logrus.Debugf("Successfull upgraded websocket")
|
||||||
|
|
||||||
|
ticker := time.NewTicker(pingPeriod)
|
||||||
|
logc := make(chan []byte, 10)
|
||||||
|
|
||||||
|
ctx, cancel := context.WithCancel(
|
||||||
|
context.Background(),
|
||||||
|
)
|
||||||
|
defer func() {
|
||||||
|
cancel()
|
||||||
|
ticker.Stop()
|
||||||
|
close(logc)
|
||||||
|
logrus.Debugf("Successfully closing websocket")
|
||||||
|
}()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
// TODO remove global variable
|
||||||
|
config.logger.Tail(ctx, fmt.Sprint(job.ID), func(entries ...*logging.Entry) {
|
||||||
|
for _, entry := range entries {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
logc <- entry.Data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
cancel()
|
||||||
|
}()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return
|
||||||
|
case buf, ok := <-logc:
|
||||||
|
if ok {
|
||||||
|
ws.SetWriteDeadline(time.Now().Add(writeWait))
|
||||||
|
ws.WriteMessage(websocket.TextMessage, buf)
|
||||||
|
}
|
||||||
|
case <-ticker.C:
|
||||||
|
err := ws.WriteControl(websocket.PingMessage, []byte{}, time.Now().Add(writeWait))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
reader(ws)
|
||||||
|
}
|
||||||
|
|
||||||
|
func EventStream2(c *gin.Context) {
|
||||||
|
ws, err := upgrader.Upgrade(c.Writer, c.Request, nil)
|
||||||
|
if err != nil {
|
||||||
|
if _, ok := err.(websocket.HandshakeError); !ok {
|
||||||
|
logrus.Errorf("Cannot upgrade websocket. %s", err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
logrus.Debugf("Successfull upgraded websocket")
|
||||||
|
|
||||||
|
user := session.User(c)
|
||||||
|
repo := map[string]bool{}
|
||||||
|
if user != nil {
|
||||||
|
repo, _ = cache.GetRepoMap(c, user)
|
||||||
|
}
|
||||||
|
|
||||||
|
ticker := time.NewTicker(pingPeriod)
|
||||||
|
eventc := make(chan []byte, 10)
|
||||||
|
|
||||||
|
ctx, cancel := context.WithCancel(
|
||||||
|
context.Background(),
|
||||||
|
)
|
||||||
|
defer func() {
|
||||||
|
cancel()
|
||||||
|
ticker.Stop()
|
||||||
|
close(eventc)
|
||||||
|
logrus.Debugf("Successfully closing websocket")
|
||||||
|
}()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
// TODO remove this from global config
|
||||||
|
config.pubsub.Subscribe(c, "topic/events", func(m pubsub.Message) {
|
||||||
|
name := m.Labels["repo"]
|
||||||
|
priv := m.Labels["private"]
|
||||||
|
if repo[name] || priv == "false" {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
eventc <- m.Data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
cancel()
|
||||||
|
}()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return
|
||||||
|
case buf, ok := <-eventc:
|
||||||
|
if ok {
|
||||||
|
ws.SetWriteDeadline(time.Now().Add(writeWait))
|
||||||
|
ws.WriteMessage(websocket.TextMessage, buf)
|
||||||
|
}
|
||||||
|
case <-ticker.C:
|
||||||
|
err := ws.WriteControl(websocket.PingMessage, []byte{}, time.Now().Add(writeWait))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
reader(ws)
|
||||||
|
}
|
||||||
|
8
store/datastore/ddl/mysql/11.sql
Normal file
8
store/datastore/ddl/mysql/11.sql
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
-- +migrate Up
|
||||||
|
|
||||||
|
ALTER TABLE builds ADD COLUMN build_error VARCHAR(500);
|
||||||
|
UPDATE builds SET build_error = '';
|
||||||
|
|
||||||
|
-- +migrate Down
|
||||||
|
|
||||||
|
ALTER TABLE builds DROP COLUMN build_error;
|
8
store/datastore/ddl/postgres/11.sql
Normal file
8
store/datastore/ddl/postgres/11.sql
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
-- +migrate Up
|
||||||
|
|
||||||
|
ALTER TABLE builds ADD COLUMN build_error VARCHAR(500);
|
||||||
|
UPDATE builds SET build_error = '';
|
||||||
|
|
||||||
|
-- +migrate Down
|
||||||
|
|
||||||
|
ALTER TABLE builds DROP COLUMN build_error;
|
8
store/datastore/ddl/sqlite3/11.sql
Normal file
8
store/datastore/ddl/sqlite3/11.sql
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
-- +migrate Up
|
||||||
|
|
||||||
|
ALTER TABLE builds ADD COLUMN build_error TEXT;
|
||||||
|
UPDATE builds SET build_error = '';
|
||||||
|
|
||||||
|
-- +migrate Down
|
||||||
|
|
||||||
|
ALTER TABLE builds DROP COLUMN build_error;
|
22
vendor/github.com/Microsoft/go-winio/LICENSE
generated
vendored
Normal file
22
vendor/github.com/Microsoft/go-winio/LICENSE
generated
vendored
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2015 Microsoft
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
|
268
vendor/github.com/Microsoft/go-winio/backup.go
generated
vendored
Normal file
268
vendor/github.com/Microsoft/go-winio/backup.go
generated
vendored
Normal file
@ -0,0 +1,268 @@
|
|||||||
|
// +build windows
|
||||||
|
|
||||||
|
package winio
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"runtime"
|
||||||
|
"syscall"
|
||||||
|
"unicode/utf16"
|
||||||
|
)
|
||||||
|
|
||||||
|
//sys backupRead(h syscall.Handle, b []byte, bytesRead *uint32, abort bool, processSecurity bool, context *uintptr) (err error) = BackupRead
|
||||||
|
//sys backupWrite(h syscall.Handle, b []byte, bytesWritten *uint32, abort bool, processSecurity bool, context *uintptr) (err error) = BackupWrite
|
||||||
|
|
||||||
|
const (
|
||||||
|
BackupData = uint32(iota + 1)
|
||||||
|
BackupEaData
|
||||||
|
BackupSecurity
|
||||||
|
BackupAlternateData
|
||||||
|
BackupLink
|
||||||
|
BackupPropertyData
|
||||||
|
BackupObjectId
|
||||||
|
BackupReparseData
|
||||||
|
BackupSparseBlock
|
||||||
|
BackupTxfsData
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
StreamSparseAttributes = uint32(8)
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
WRITE_DAC = 0x40000
|
||||||
|
WRITE_OWNER = 0x80000
|
||||||
|
ACCESS_SYSTEM_SECURITY = 0x1000000
|
||||||
|
)
|
||||||
|
|
||||||
|
// BackupHeader represents a backup stream of a file.
|
||||||
|
type BackupHeader struct {
|
||||||
|
Id uint32 // The backup stream ID
|
||||||
|
Attributes uint32 // Stream attributes
|
||||||
|
Size int64 // The size of the stream in bytes
|
||||||
|
Name string // The name of the stream (for BackupAlternateData only).
|
||||||
|
Offset int64 // The offset of the stream in the file (for BackupSparseBlock only).
|
||||||
|
}
|
||||||
|
|
||||||
|
type win32StreamId struct {
|
||||||
|
StreamId uint32
|
||||||
|
Attributes uint32
|
||||||
|
Size uint64
|
||||||
|
NameSize uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// BackupStreamReader reads from a stream produced by the BackupRead Win32 API and produces a series
|
||||||
|
// of BackupHeader values.
|
||||||
|
type BackupStreamReader struct {
|
||||||
|
r io.Reader
|
||||||
|
bytesLeft int64
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewBackupStreamReader produces a BackupStreamReader from any io.Reader.
|
||||||
|
func NewBackupStreamReader(r io.Reader) *BackupStreamReader {
|
||||||
|
return &BackupStreamReader{r, 0}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next returns the next backup stream and prepares for calls to Write(). It skips the remainder of the current stream if
|
||||||
|
// it was not completely read.
|
||||||
|
func (r *BackupStreamReader) Next() (*BackupHeader, error) {
|
||||||
|
if r.bytesLeft > 0 {
|
||||||
|
if _, err := io.Copy(ioutil.Discard, r); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var wsi win32StreamId
|
||||||
|
if err := binary.Read(r.r, binary.LittleEndian, &wsi); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
hdr := &BackupHeader{
|
||||||
|
Id: wsi.StreamId,
|
||||||
|
Attributes: wsi.Attributes,
|
||||||
|
Size: int64(wsi.Size),
|
||||||
|
}
|
||||||
|
if wsi.NameSize != 0 {
|
||||||
|
name := make([]uint16, int(wsi.NameSize/2))
|
||||||
|
if err := binary.Read(r.r, binary.LittleEndian, name); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
hdr.Name = syscall.UTF16ToString(name)
|
||||||
|
}
|
||||||
|
if wsi.StreamId == BackupSparseBlock {
|
||||||
|
if err := binary.Read(r.r, binary.LittleEndian, &hdr.Offset); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
hdr.Size -= 8
|
||||||
|
}
|
||||||
|
r.bytesLeft = hdr.Size
|
||||||
|
return hdr, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read reads from the current backup stream.
|
||||||
|
func (r *BackupStreamReader) Read(b []byte) (int, error) {
|
||||||
|
if r.bytesLeft == 0 {
|
||||||
|
return 0, io.EOF
|
||||||
|
}
|
||||||
|
if int64(len(b)) > r.bytesLeft {
|
||||||
|
b = b[:r.bytesLeft]
|
||||||
|
}
|
||||||
|
n, err := r.r.Read(b)
|
||||||
|
r.bytesLeft -= int64(n)
|
||||||
|
if err == io.EOF {
|
||||||
|
err = io.ErrUnexpectedEOF
|
||||||
|
} else if r.bytesLeft == 0 && err == nil {
|
||||||
|
err = io.EOF
|
||||||
|
}
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// BackupStreamWriter writes a stream compatible with the BackupWrite Win32 API.
|
||||||
|
type BackupStreamWriter struct {
|
||||||
|
w io.Writer
|
||||||
|
bytesLeft int64
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewBackupStreamWriter produces a BackupStreamWriter on top of an io.Writer.
|
||||||
|
func NewBackupStreamWriter(w io.Writer) *BackupStreamWriter {
|
||||||
|
return &BackupStreamWriter{w, 0}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteHeader writes the next backup stream header and prepares for calls to Write().
|
||||||
|
func (w *BackupStreamWriter) WriteHeader(hdr *BackupHeader) error {
|
||||||
|
if w.bytesLeft != 0 {
|
||||||
|
return fmt.Errorf("missing %d bytes", w.bytesLeft)
|
||||||
|
}
|
||||||
|
name := utf16.Encode([]rune(hdr.Name))
|
||||||
|
wsi := win32StreamId{
|
||||||
|
StreamId: hdr.Id,
|
||||||
|
Attributes: hdr.Attributes,
|
||||||
|
Size: uint64(hdr.Size),
|
||||||
|
NameSize: uint32(len(name) * 2),
|
||||||
|
}
|
||||||
|
if hdr.Id == BackupSparseBlock {
|
||||||
|
// Include space for the int64 block offset
|
||||||
|
wsi.Size += 8
|
||||||
|
}
|
||||||
|
if err := binary.Write(w.w, binary.LittleEndian, &wsi); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(name) != 0 {
|
||||||
|
if err := binary.Write(w.w, binary.LittleEndian, name); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if hdr.Id == BackupSparseBlock {
|
||||||
|
if err := binary.Write(w.w, binary.LittleEndian, hdr.Offset); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
w.bytesLeft = hdr.Size
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write writes to the current backup stream.
|
||||||
|
func (w *BackupStreamWriter) Write(b []byte) (int, error) {
|
||||||
|
if w.bytesLeft < int64(len(b)) {
|
||||||
|
return 0, fmt.Errorf("too many bytes by %d", int64(len(b))-w.bytesLeft)
|
||||||
|
}
|
||||||
|
n, err := w.w.Write(b)
|
||||||
|
w.bytesLeft -= int64(n)
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// BackupFileReader provides an io.ReadCloser interface on top of the BackupRead Win32 API.
|
||||||
|
type BackupFileReader struct {
|
||||||
|
f *os.File
|
||||||
|
includeSecurity bool
|
||||||
|
ctx uintptr
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewBackupFileReader returns a new BackupFileReader from a file handle. If includeSecurity is true,
|
||||||
|
// Read will attempt to read the security descriptor of the file.
|
||||||
|
func NewBackupFileReader(f *os.File, includeSecurity bool) *BackupFileReader {
|
||||||
|
r := &BackupFileReader{f, includeSecurity, 0}
|
||||||
|
runtime.SetFinalizer(r, func(r *BackupFileReader) { r.Close() })
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read reads a backup stream from the file by calling the Win32 API BackupRead().
|
||||||
|
func (r *BackupFileReader) Read(b []byte) (int, error) {
|
||||||
|
var bytesRead uint32
|
||||||
|
err := backupRead(syscall.Handle(r.f.Fd()), b, &bytesRead, false, r.includeSecurity, &r.ctx)
|
||||||
|
if err != nil {
|
||||||
|
return 0, &os.PathError{"BackupRead", r.f.Name(), err}
|
||||||
|
}
|
||||||
|
if bytesRead == 0 {
|
||||||
|
return 0, io.EOF
|
||||||
|
}
|
||||||
|
return int(bytesRead), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close frees Win32 resources associated with the BackupFileReader. It does not close
|
||||||
|
// the underlying file.
|
||||||
|
func (r *BackupFileReader) Close() error {
|
||||||
|
if r.ctx != 0 {
|
||||||
|
backupRead(syscall.Handle(r.f.Fd()), nil, nil, true, false, &r.ctx)
|
||||||
|
r.ctx = 0
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// BackupFileWriter provides an io.WriteCloser interface on top of the BackupWrite Win32 API.
|
||||||
|
type BackupFileWriter struct {
|
||||||
|
f *os.File
|
||||||
|
includeSecurity bool
|
||||||
|
ctx uintptr
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewBackupFileWrtier returns a new BackupFileWriter from a file handle. If includeSecurity is true,
|
||||||
|
// Write() will attempt to restore the security descriptor from the stream.
|
||||||
|
func NewBackupFileWriter(f *os.File, includeSecurity bool) *BackupFileWriter {
|
||||||
|
w := &BackupFileWriter{f, includeSecurity, 0}
|
||||||
|
runtime.SetFinalizer(w, func(w *BackupFileWriter) { w.Close() })
|
||||||
|
return w
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write restores a portion of the file using the provided backup stream.
|
||||||
|
func (w *BackupFileWriter) Write(b []byte) (int, error) {
|
||||||
|
var bytesWritten uint32
|
||||||
|
err := backupWrite(syscall.Handle(w.f.Fd()), b, &bytesWritten, false, w.includeSecurity, &w.ctx)
|
||||||
|
if err != nil {
|
||||||
|
return 0, &os.PathError{"BackupWrite", w.f.Name(), err}
|
||||||
|
}
|
||||||
|
if int(bytesWritten) != len(b) {
|
||||||
|
return int(bytesWritten), errors.New("not all bytes could be written")
|
||||||
|
}
|
||||||
|
return len(b), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close frees Win32 resources associated with the BackupFileWriter. It does not
|
||||||
|
// close the underlying file.
|
||||||
|
func (w *BackupFileWriter) Close() error {
|
||||||
|
if w.ctx != 0 {
|
||||||
|
backupWrite(syscall.Handle(w.f.Fd()), nil, nil, true, false, &w.ctx)
|
||||||
|
w.ctx = 0
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// OpenForBackup opens a file or directory, potentially skipping access checks if the backup
|
||||||
|
// or restore privileges have been acquired.
|
||||||
|
//
|
||||||
|
// If the file opened was a directory, it cannot be used with Readdir().
|
||||||
|
func OpenForBackup(path string, access uint32, share uint32, createmode uint32) (*os.File, error) {
|
||||||
|
winPath, err := syscall.UTF16FromString(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
h, err := syscall.CreateFile(&winPath[0], access, share, nil, createmode, syscall.FILE_FLAG_BACKUP_SEMANTICS|syscall.FILE_FLAG_OPEN_REPARSE_POINT, 0)
|
||||||
|
if err != nil {
|
||||||
|
err = &os.PathError{Op: "open", Path: path, Err: err}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return os.NewFile(uintptr(h), path), nil
|
||||||
|
}
|
221
vendor/github.com/Microsoft/go-winio/file.go
generated
vendored
Normal file
221
vendor/github.com/Microsoft/go-winio/file.go
generated
vendored
Normal file
@ -0,0 +1,221 @@
|
|||||||
|
// +build windows
|
||||||
|
|
||||||
|
package winio
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"runtime"
|
||||||
|
"sync"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
//sys cancelIoEx(file syscall.Handle, o *syscall.Overlapped) (err error) = CancelIoEx
|
||||||
|
//sys createIoCompletionPort(file syscall.Handle, port syscall.Handle, key uintptr, threadCount uint32) (newport syscall.Handle, err error) = CreateIoCompletionPort
|
||||||
|
//sys getQueuedCompletionStatus(port syscall.Handle, bytes *uint32, key *uintptr, o **ioOperation, timeout uint32) (err error) = GetQueuedCompletionStatus
|
||||||
|
//sys setFileCompletionNotificationModes(h syscall.Handle, flags uint8) (err error) = SetFileCompletionNotificationModes
|
||||||
|
//sys timeBeginPeriod(period uint32) (n int32) = winmm.timeBeginPeriod
|
||||||
|
|
||||||
|
const (
|
||||||
|
cFILE_SKIP_COMPLETION_PORT_ON_SUCCESS = 1
|
||||||
|
cFILE_SKIP_SET_EVENT_ON_HANDLE = 2
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrFileClosed = errors.New("file has already been closed")
|
||||||
|
ErrTimeout = &timeoutError{}
|
||||||
|
)
|
||||||
|
|
||||||
|
type timeoutError struct{}
|
||||||
|
|
||||||
|
func (e *timeoutError) Error() string { return "i/o timeout" }
|
||||||
|
func (e *timeoutError) Timeout() bool { return true }
|
||||||
|
func (e *timeoutError) Temporary() bool { return true }
|
||||||
|
|
||||||
|
var ioInitOnce sync.Once
|
||||||
|
var ioCompletionPort syscall.Handle
|
||||||
|
|
||||||
|
// ioResult contains the result of an asynchronous IO operation
|
||||||
|
type ioResult struct {
|
||||||
|
bytes uint32
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
// ioOperation represents an outstanding asynchronous Win32 IO
|
||||||
|
type ioOperation struct {
|
||||||
|
o syscall.Overlapped
|
||||||
|
ch chan ioResult
|
||||||
|
}
|
||||||
|
|
||||||
|
func initIo() {
|
||||||
|
h, err := createIoCompletionPort(syscall.InvalidHandle, 0, 0, 0xffffffff)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
ioCompletionPort = h
|
||||||
|
go ioCompletionProcessor(h)
|
||||||
|
}
|
||||||
|
|
||||||
|
// win32File implements Reader, Writer, and Closer on a Win32 handle without blocking in a syscall.
|
||||||
|
// It takes ownership of this handle and will close it if it is garbage collected.
|
||||||
|
type win32File struct {
|
||||||
|
handle syscall.Handle
|
||||||
|
wg sync.WaitGroup
|
||||||
|
closing bool
|
||||||
|
readDeadline time.Time
|
||||||
|
writeDeadline time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
// makeWin32File makes a new win32File from an existing file handle
|
||||||
|
func makeWin32File(h syscall.Handle) (*win32File, error) {
|
||||||
|
f := &win32File{handle: h}
|
||||||
|
ioInitOnce.Do(initIo)
|
||||||
|
_, err := createIoCompletionPort(h, ioCompletionPort, 0, 0xffffffff)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
err = setFileCompletionNotificationModes(h, cFILE_SKIP_COMPLETION_PORT_ON_SUCCESS|cFILE_SKIP_SET_EVENT_ON_HANDLE)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
runtime.SetFinalizer(f, (*win32File).closeHandle)
|
||||||
|
return f, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func MakeOpenFile(h syscall.Handle) (io.ReadWriteCloser, error) {
|
||||||
|
return makeWin32File(h)
|
||||||
|
}
|
||||||
|
|
||||||
|
// closeHandle closes the resources associated with a Win32 handle
|
||||||
|
func (f *win32File) closeHandle() {
|
||||||
|
if !f.closing {
|
||||||
|
// cancel all IO and wait for it to complete
|
||||||
|
f.closing = true
|
||||||
|
cancelIoEx(f.handle, nil)
|
||||||
|
f.wg.Wait()
|
||||||
|
// at this point, no new IO can start
|
||||||
|
syscall.Close(f.handle)
|
||||||
|
f.handle = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close closes a win32File.
|
||||||
|
func (f *win32File) Close() error {
|
||||||
|
f.closeHandle()
|
||||||
|
runtime.SetFinalizer(f, nil)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// prepareIo prepares for a new IO operation
|
||||||
|
func (f *win32File) prepareIo() (*ioOperation, error) {
|
||||||
|
f.wg.Add(1)
|
||||||
|
if f.closing {
|
||||||
|
return nil, ErrFileClosed
|
||||||
|
}
|
||||||
|
c := &ioOperation{}
|
||||||
|
c.ch = make(chan ioResult)
|
||||||
|
return c, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ioCompletionProcessor processes completed async IOs forever
|
||||||
|
func ioCompletionProcessor(h syscall.Handle) {
|
||||||
|
// Set the timer resolution to 1. This fixes a performance regression in golang 1.6.
|
||||||
|
timeBeginPeriod(1)
|
||||||
|
for {
|
||||||
|
var bytes uint32
|
||||||
|
var key uintptr
|
||||||
|
var op *ioOperation
|
||||||
|
err := getQueuedCompletionStatus(h, &bytes, &key, &op, syscall.INFINITE)
|
||||||
|
if op == nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
op.ch <- ioResult{bytes, err}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// asyncIo processes the return value from ReadFile or WriteFile, blocking until
|
||||||
|
// the operation has actually completed.
|
||||||
|
func (f *win32File) asyncIo(c *ioOperation, deadline time.Time, bytes uint32, err error) (int, error) {
|
||||||
|
if err != syscall.ERROR_IO_PENDING {
|
||||||
|
f.wg.Done()
|
||||||
|
return int(bytes), err
|
||||||
|
} else {
|
||||||
|
var r ioResult
|
||||||
|
wait := true
|
||||||
|
timedout := false
|
||||||
|
if f.closing {
|
||||||
|
cancelIoEx(f.handle, &c.o)
|
||||||
|
} else if !deadline.IsZero() {
|
||||||
|
now := time.Now()
|
||||||
|
if !deadline.After(now) {
|
||||||
|
timedout = true
|
||||||
|
} else {
|
||||||
|
timeout := time.After(deadline.Sub(now))
|
||||||
|
select {
|
||||||
|
case r = <-c.ch:
|
||||||
|
wait = false
|
||||||
|
case <-timeout:
|
||||||
|
timedout = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if timedout {
|
||||||
|
cancelIoEx(f.handle, &c.o)
|
||||||
|
}
|
||||||
|
if wait {
|
||||||
|
r = <-c.ch
|
||||||
|
}
|
||||||
|
err = r.err
|
||||||
|
if err == syscall.ERROR_OPERATION_ABORTED {
|
||||||
|
if f.closing {
|
||||||
|
err = ErrFileClosed
|
||||||
|
} else if timedout {
|
||||||
|
err = ErrTimeout
|
||||||
|
}
|
||||||
|
}
|
||||||
|
f.wg.Done()
|
||||||
|
return int(r.bytes), err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read reads from a file handle.
|
||||||
|
func (f *win32File) Read(b []byte) (int, error) {
|
||||||
|
c, err := f.prepareIo()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
var bytes uint32
|
||||||
|
err = syscall.ReadFile(f.handle, b, &bytes, &c.o)
|
||||||
|
n, err := f.asyncIo(c, f.readDeadline, bytes, err)
|
||||||
|
|
||||||
|
// Handle EOF conditions.
|
||||||
|
if err == nil && n == 0 && len(b) != 0 {
|
||||||
|
return 0, io.EOF
|
||||||
|
} else if err == syscall.ERROR_BROKEN_PIPE {
|
||||||
|
return 0, io.EOF
|
||||||
|
} else {
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write writes to a file handle.
|
||||||
|
func (f *win32File) Write(b []byte) (int, error) {
|
||||||
|
c, err := f.prepareIo()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
var bytes uint32
|
||||||
|
err = syscall.WriteFile(f.handle, b, &bytes, &c.o)
|
||||||
|
return f.asyncIo(c, f.writeDeadline, bytes, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *win32File) SetReadDeadline(t time.Time) error {
|
||||||
|
f.readDeadline = t
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *win32File) SetWriteDeadline(t time.Time) error {
|
||||||
|
f.writeDeadline = t
|
||||||
|
return nil
|
||||||
|
}
|
56
vendor/github.com/Microsoft/go-winio/fileinfo.go
generated
vendored
Normal file
56
vendor/github.com/Microsoft/go-winio/fileinfo.go
generated
vendored
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
// +build windows
|
||||||
|
|
||||||
|
package winio
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
//sys getFileInformationByHandleEx(h syscall.Handle, class uint32, buffer *byte, size uint32) (err error) = GetFileInformationByHandleEx
|
||||||
|
//sys setFileInformationByHandle(h syscall.Handle, class uint32, buffer *byte, size uint32) (err error) = SetFileInformationByHandle
|
||||||
|
|
||||||
|
const (
|
||||||
|
fileBasicInfo = 0
|
||||||
|
fileIDInfo = 0x12
|
||||||
|
)
|
||||||
|
|
||||||
|
// FileBasicInfo contains file access time and file attributes information.
|
||||||
|
type FileBasicInfo struct {
|
||||||
|
CreationTime, LastAccessTime, LastWriteTime, ChangeTime syscall.Filetime
|
||||||
|
FileAttributes uintptr // includes padding
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetFileBasicInfo retrieves times and attributes for a file.
|
||||||
|
func GetFileBasicInfo(f *os.File) (*FileBasicInfo, error) {
|
||||||
|
bi := &FileBasicInfo{}
|
||||||
|
if err := getFileInformationByHandleEx(syscall.Handle(f.Fd()), fileBasicInfo, (*byte)(unsafe.Pointer(bi)), uint32(unsafe.Sizeof(*bi))); err != nil {
|
||||||
|
return nil, &os.PathError{Op: "GetFileInformationByHandleEx", Path: f.Name(), Err: err}
|
||||||
|
}
|
||||||
|
return bi, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetFileBasicInfo sets times and attributes for a file.
|
||||||
|
func SetFileBasicInfo(f *os.File, bi *FileBasicInfo) error {
|
||||||
|
if err := setFileInformationByHandle(syscall.Handle(f.Fd()), fileBasicInfo, (*byte)(unsafe.Pointer(bi)), uint32(unsafe.Sizeof(*bi))); err != nil {
|
||||||
|
return &os.PathError{Op: "SetFileInformationByHandle", Path: f.Name(), Err: err}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// FileIDInfo contains the volume serial number and file ID for a file. This pair should be
|
||||||
|
// unique on a system.
|
||||||
|
type FileIDInfo struct {
|
||||||
|
VolumeSerialNumber uint64
|
||||||
|
FileID [16]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetFileID retrieves the unique (volume, file ID) pair for a file.
|
||||||
|
func GetFileID(f *os.File) (*FileIDInfo, error) {
|
||||||
|
fileID := &FileIDInfo{}
|
||||||
|
if err := getFileInformationByHandleEx(syscall.Handle(f.Fd()), fileIDInfo, (*byte)(unsafe.Pointer(fileID)), uint32(unsafe.Sizeof(*fileID))); err != nil {
|
||||||
|
return nil, &os.PathError{Op: "GetFileInformationByHandleEx", Path: f.Name(), Err: err}
|
||||||
|
}
|
||||||
|
return fileID, nil
|
||||||
|
}
|
400
vendor/github.com/Microsoft/go-winio/pipe.go
generated
vendored
Normal file
400
vendor/github.com/Microsoft/go-winio/pipe.go
generated
vendored
Normal file
@ -0,0 +1,400 @@
|
|||||||
|
// +build windows
|
||||||
|
|
||||||
|
package winio
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
//sys connectNamedPipe(pipe syscall.Handle, o *syscall.Overlapped) (err error) = ConnectNamedPipe
|
||||||
|
//sys createNamedPipe(name string, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *securityAttributes) (handle syscall.Handle, err error) [failretval==syscall.InvalidHandle] = CreateNamedPipeW
|
||||||
|
//sys createFile(name string, access uint32, mode uint32, sa *securityAttributes, createmode uint32, attrs uint32, templatefile syscall.Handle) (handle syscall.Handle, err error) [failretval==syscall.InvalidHandle] = CreateFileW
|
||||||
|
//sys waitNamedPipe(name string, timeout uint32) (err error) = WaitNamedPipeW
|
||||||
|
//sys getNamedPipeInfo(pipe syscall.Handle, flags *uint32, outSize *uint32, inSize *uint32, maxInstances *uint32) (err error) = GetNamedPipeInfo
|
||||||
|
//sys getNamedPipeHandleState(pipe syscall.Handle, state *uint32, curInstances *uint32, maxCollectionCount *uint32, collectDataTimeout *uint32, userName *uint16, maxUserNameSize uint32) (err error) = GetNamedPipeHandleStateW
|
||||||
|
|
||||||
|
type securityAttributes struct {
|
||||||
|
Length uint32
|
||||||
|
SecurityDescriptor *byte
|
||||||
|
InheritHandle uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
cERROR_PIPE_BUSY = syscall.Errno(231)
|
||||||
|
cERROR_PIPE_CONNECTED = syscall.Errno(535)
|
||||||
|
cERROR_SEM_TIMEOUT = syscall.Errno(121)
|
||||||
|
|
||||||
|
cPIPE_ACCESS_DUPLEX = 0x3
|
||||||
|
cFILE_FLAG_FIRST_PIPE_INSTANCE = 0x80000
|
||||||
|
cSECURITY_SQOS_PRESENT = 0x100000
|
||||||
|
cSECURITY_ANONYMOUS = 0
|
||||||
|
|
||||||
|
cPIPE_REJECT_REMOTE_CLIENTS = 0x8
|
||||||
|
|
||||||
|
cPIPE_UNLIMITED_INSTANCES = 255
|
||||||
|
|
||||||
|
cNMPWAIT_USE_DEFAULT_WAIT = 0
|
||||||
|
cNMPWAIT_NOWAIT = 1
|
||||||
|
|
||||||
|
cPIPE_TYPE_MESSAGE = 4
|
||||||
|
|
||||||
|
cPIPE_READMODE_MESSAGE = 2
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ErrPipeListenerClosed is returned for pipe operations on listeners that have been closed.
|
||||||
|
// This error should match net.errClosing since docker takes a dependency on its text.
|
||||||
|
ErrPipeListenerClosed = errors.New("use of closed network connection")
|
||||||
|
|
||||||
|
errPipeWriteClosed = errors.New("pipe has been closed for write")
|
||||||
|
)
|
||||||
|
|
||||||
|
type win32Pipe struct {
|
||||||
|
*win32File
|
||||||
|
path string
|
||||||
|
}
|
||||||
|
|
||||||
|
type win32MessageBytePipe struct {
|
||||||
|
win32Pipe
|
||||||
|
writeClosed bool
|
||||||
|
readEOF bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type pipeAddress string
|
||||||
|
|
||||||
|
func (f *win32Pipe) LocalAddr() net.Addr {
|
||||||
|
return pipeAddress(f.path)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *win32Pipe) RemoteAddr() net.Addr {
|
||||||
|
return pipeAddress(f.path)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *win32Pipe) SetDeadline(t time.Time) error {
|
||||||
|
f.SetReadDeadline(t)
|
||||||
|
f.SetWriteDeadline(t)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CloseWrite closes the write side of a message pipe in byte mode.
|
||||||
|
func (f *win32MessageBytePipe) CloseWrite() error {
|
||||||
|
if f.writeClosed {
|
||||||
|
return errPipeWriteClosed
|
||||||
|
}
|
||||||
|
_, err := f.win32File.Write(nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
f.writeClosed = true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write writes bytes to a message pipe in byte mode. Zero-byte writes are ignored, since
|
||||||
|
// they are used to implement CloseWrite().
|
||||||
|
func (f *win32MessageBytePipe) Write(b []byte) (int, error) {
|
||||||
|
if f.writeClosed {
|
||||||
|
return 0, errPipeWriteClosed
|
||||||
|
}
|
||||||
|
if len(b) == 0 {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
return f.win32File.Write(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read reads bytes from a message pipe in byte mode. A read of a zero-byte message on a message
|
||||||
|
// mode pipe will return io.EOF, as will all subsequent reads.
|
||||||
|
func (f *win32MessageBytePipe) Read(b []byte) (int, error) {
|
||||||
|
if f.readEOF {
|
||||||
|
return 0, io.EOF
|
||||||
|
}
|
||||||
|
n, err := f.win32File.Read(b)
|
||||||
|
if err == io.EOF {
|
||||||
|
// If this was the result of a zero-byte read, then
|
||||||
|
// it is possible that the read was due to a zero-size
|
||||||
|
// message. Since we are simulating CloseWrite with a
|
||||||
|
// zero-byte message, ensure that all future Read() calls
|
||||||
|
// also return EOF.
|
||||||
|
f.readEOF = true
|
||||||
|
}
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s pipeAddress) Network() string {
|
||||||
|
return "pipe"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s pipeAddress) String() string {
|
||||||
|
return string(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DialPipe connects to a named pipe by path, timing out if the connection
|
||||||
|
// takes longer than the specified duration. If timeout is nil, then the timeout
|
||||||
|
// is the default timeout established by the pipe server.
|
||||||
|
func DialPipe(path string, timeout *time.Duration) (net.Conn, error) {
|
||||||
|
var absTimeout time.Time
|
||||||
|
if timeout != nil {
|
||||||
|
absTimeout = time.Now().Add(*timeout)
|
||||||
|
}
|
||||||
|
var err error
|
||||||
|
var h syscall.Handle
|
||||||
|
for {
|
||||||
|
h, err = createFile(path, syscall.GENERIC_READ|syscall.GENERIC_WRITE, 0, nil, syscall.OPEN_EXISTING, syscall.FILE_FLAG_OVERLAPPED|cSECURITY_SQOS_PRESENT|cSECURITY_ANONYMOUS, 0)
|
||||||
|
if err != cERROR_PIPE_BUSY {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
now := time.Now()
|
||||||
|
var ms uint32
|
||||||
|
if absTimeout.IsZero() {
|
||||||
|
ms = cNMPWAIT_USE_DEFAULT_WAIT
|
||||||
|
} else if now.After(absTimeout) {
|
||||||
|
ms = cNMPWAIT_NOWAIT
|
||||||
|
} else {
|
||||||
|
ms = uint32(absTimeout.Sub(now).Nanoseconds() / 1000 / 1000)
|
||||||
|
}
|
||||||
|
err = waitNamedPipe(path, ms)
|
||||||
|
if err != nil {
|
||||||
|
if err == cERROR_SEM_TIMEOUT {
|
||||||
|
return nil, ErrTimeout
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, &os.PathError{Op: "open", Path: path, Err: err}
|
||||||
|
}
|
||||||
|
|
||||||
|
var flags uint32
|
||||||
|
err = getNamedPipeInfo(h, &flags, nil, nil, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var state uint32
|
||||||
|
err = getNamedPipeHandleState(h, &state, nil, nil, nil, nil, 0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if state&cPIPE_READMODE_MESSAGE != 0 {
|
||||||
|
return nil, &os.PathError{Op: "open", Path: path, Err: errors.New("message readmode pipes not supported")}
|
||||||
|
}
|
||||||
|
|
||||||
|
f, err := makeWin32File(h)
|
||||||
|
if err != nil {
|
||||||
|
syscall.Close(h)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the pipe is in message mode, return a message byte pipe, which
|
||||||
|
// supports CloseWrite().
|
||||||
|
if flags&cPIPE_TYPE_MESSAGE != 0 {
|
||||||
|
return &win32MessageBytePipe{
|
||||||
|
win32Pipe: win32Pipe{win32File: f, path: path},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
return &win32Pipe{win32File: f, path: path}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type acceptResponse struct {
|
||||||
|
f *win32File
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
type win32PipeListener struct {
|
||||||
|
firstHandle syscall.Handle
|
||||||
|
path string
|
||||||
|
securityDescriptor []byte
|
||||||
|
config PipeConfig
|
||||||
|
acceptCh chan (chan acceptResponse)
|
||||||
|
closeCh chan int
|
||||||
|
doneCh chan int
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeServerPipeHandle(path string, securityDescriptor []byte, c *PipeConfig, first bool) (syscall.Handle, error) {
|
||||||
|
var flags uint32 = cPIPE_ACCESS_DUPLEX | syscall.FILE_FLAG_OVERLAPPED
|
||||||
|
if first {
|
||||||
|
flags |= cFILE_FLAG_FIRST_PIPE_INSTANCE
|
||||||
|
}
|
||||||
|
|
||||||
|
var mode uint32 = cPIPE_REJECT_REMOTE_CLIENTS
|
||||||
|
if c.MessageMode {
|
||||||
|
mode |= cPIPE_TYPE_MESSAGE
|
||||||
|
}
|
||||||
|
|
||||||
|
var sa securityAttributes
|
||||||
|
sa.Length = uint32(unsafe.Sizeof(sa))
|
||||||
|
if securityDescriptor != nil {
|
||||||
|
sa.SecurityDescriptor = &securityDescriptor[0]
|
||||||
|
}
|
||||||
|
h, err := createNamedPipe(path, flags, mode, cPIPE_UNLIMITED_INSTANCES, uint32(c.OutputBufferSize), uint32(c.InputBufferSize), 0, &sa)
|
||||||
|
if err != nil {
|
||||||
|
return 0, &os.PathError{Op: "open", Path: path, Err: err}
|
||||||
|
}
|
||||||
|
return h, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *win32PipeListener) makeServerPipe() (*win32File, error) {
|
||||||
|
h, err := makeServerPipeHandle(l.path, l.securityDescriptor, &l.config, false)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
f, err := makeWin32File(h)
|
||||||
|
if err != nil {
|
||||||
|
syscall.Close(h)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return f, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *win32PipeListener) listenerRoutine() {
|
||||||
|
closed := false
|
||||||
|
for !closed {
|
||||||
|
select {
|
||||||
|
case <-l.closeCh:
|
||||||
|
closed = true
|
||||||
|
case responseCh := <-l.acceptCh:
|
||||||
|
p, err := l.makeServerPipe()
|
||||||
|
if err == nil {
|
||||||
|
// Wait for the client to connect.
|
||||||
|
ch := make(chan error)
|
||||||
|
go func() {
|
||||||
|
ch <- connectPipe(p)
|
||||||
|
}()
|
||||||
|
select {
|
||||||
|
case err = <-ch:
|
||||||
|
if err != nil {
|
||||||
|
p.Close()
|
||||||
|
p = nil
|
||||||
|
}
|
||||||
|
case <-l.closeCh:
|
||||||
|
// Abort the connect request by closing the handle.
|
||||||
|
p.Close()
|
||||||
|
p = nil
|
||||||
|
err = <-ch
|
||||||
|
if err == nil || err == ErrFileClosed {
|
||||||
|
err = ErrPipeListenerClosed
|
||||||
|
}
|
||||||
|
closed = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
responseCh <- acceptResponse{p, err}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
syscall.Close(l.firstHandle)
|
||||||
|
l.firstHandle = 0
|
||||||
|
// Notify Close() and Accept() callers that the handle has been closed.
|
||||||
|
close(l.doneCh)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PipeConfig contain configuration for the pipe listener.
|
||||||
|
type PipeConfig struct {
|
||||||
|
// SecurityDescriptor contains a Windows security descriptor in SDDL format.
|
||||||
|
SecurityDescriptor string
|
||||||
|
|
||||||
|
// MessageMode determines whether the pipe is in byte or message mode. In either
|
||||||
|
// case the pipe is read in byte mode by default. The only practical difference in
|
||||||
|
// this implementation is that CloseWrite() is only supported for message mode pipes;
|
||||||
|
// CloseWrite() is implemented as a zero-byte write, but zero-byte writes are only
|
||||||
|
// transferred to the reader (and returned as io.EOF in this implementation)
|
||||||
|
// when the pipe is in message mode.
|
||||||
|
MessageMode bool
|
||||||
|
|
||||||
|
// InputBufferSize specifies the size the input buffer, in bytes.
|
||||||
|
InputBufferSize int32
|
||||||
|
|
||||||
|
// OutputBufferSize specifies the size the input buffer, in bytes.
|
||||||
|
OutputBufferSize int32
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListenPipe creates a listener on a Windows named pipe path, e.g. \\.\pipe\mypipe.
|
||||||
|
// The pipe must not already exist.
|
||||||
|
func ListenPipe(path string, c *PipeConfig) (net.Listener, error) {
|
||||||
|
var (
|
||||||
|
sd []byte
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
if c == nil {
|
||||||
|
c = &PipeConfig{}
|
||||||
|
}
|
||||||
|
if c.SecurityDescriptor != "" {
|
||||||
|
sd, err = SddlToSecurityDescriptor(c.SecurityDescriptor)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
h, err := makeServerPipeHandle(path, sd, c, true)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// Immediately open and then close a client handle so that the named pipe is
|
||||||
|
// created but not currently accepting connections.
|
||||||
|
h2, err := createFile(path, 0, 0, nil, syscall.OPEN_EXISTING, cSECURITY_SQOS_PRESENT|cSECURITY_ANONYMOUS, 0)
|
||||||
|
if err != nil {
|
||||||
|
syscall.Close(h)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
syscall.Close(h2)
|
||||||
|
l := &win32PipeListener{
|
||||||
|
firstHandle: h,
|
||||||
|
path: path,
|
||||||
|
securityDescriptor: sd,
|
||||||
|
config: *c,
|
||||||
|
acceptCh: make(chan (chan acceptResponse)),
|
||||||
|
closeCh: make(chan int),
|
||||||
|
doneCh: make(chan int),
|
||||||
|
}
|
||||||
|
go l.listenerRoutine()
|
||||||
|
return l, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func connectPipe(p *win32File) error {
|
||||||
|
c, err := p.prepareIo()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = connectNamedPipe(p.handle, &c.o)
|
||||||
|
_, err = p.asyncIo(c, time.Time{}, 0, err)
|
||||||
|
if err != nil && err != cERROR_PIPE_CONNECTED {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *win32PipeListener) Accept() (net.Conn, error) {
|
||||||
|
ch := make(chan acceptResponse)
|
||||||
|
select {
|
||||||
|
case l.acceptCh <- ch:
|
||||||
|
response := <-ch
|
||||||
|
err := response.err
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if l.config.MessageMode {
|
||||||
|
return &win32MessageBytePipe{
|
||||||
|
win32Pipe: win32Pipe{win32File: response.f, path: l.path},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
return &win32Pipe{win32File: response.f, path: l.path}, nil
|
||||||
|
case <-l.doneCh:
|
||||||
|
return nil, ErrPipeListenerClosed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *win32PipeListener) Close() error {
|
||||||
|
select {
|
||||||
|
case l.closeCh <- 1:
|
||||||
|
<-l.doneCh
|
||||||
|
case <-l.doneCh:
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *win32PipeListener) Addr() net.Addr {
|
||||||
|
return pipeAddress(l.path)
|
||||||
|
}
|
202
vendor/github.com/Microsoft/go-winio/privilege.go
generated
vendored
Normal file
202
vendor/github.com/Microsoft/go-winio/privilege.go
generated
vendored
Normal file
@ -0,0 +1,202 @@
|
|||||||
|
// +build windows
|
||||||
|
|
||||||
|
package winio
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
"runtime"
|
||||||
|
"sync"
|
||||||
|
"syscall"
|
||||||
|
"unicode/utf16"
|
||||||
|
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
)
|
||||||
|
|
||||||
|
//sys adjustTokenPrivileges(token windows.Token, releaseAll bool, input *byte, outputSize uint32, output *byte, requiredSize *uint32) (success bool, err error) [true] = advapi32.AdjustTokenPrivileges
|
||||||
|
//sys impersonateSelf(level uint32) (err error) = advapi32.ImpersonateSelf
|
||||||
|
//sys revertToSelf() (err error) = advapi32.RevertToSelf
|
||||||
|
//sys openThreadToken(thread syscall.Handle, accessMask uint32, openAsSelf bool, token *windows.Token) (err error) = advapi32.OpenThreadToken
|
||||||
|
//sys getCurrentThread() (h syscall.Handle) = GetCurrentThread
|
||||||
|
//sys lookupPrivilegeValue(systemName string, name string, luid *uint64) (err error) = advapi32.LookupPrivilegeValueW
|
||||||
|
//sys lookupPrivilegeName(systemName string, luid *uint64, buffer *uint16, size *uint32) (err error) = advapi32.LookupPrivilegeNameW
|
||||||
|
//sys lookupPrivilegeDisplayName(systemName string, name *uint16, buffer *uint16, size *uint32, languageId *uint32) (err error) = advapi32.LookupPrivilegeDisplayNameW
|
||||||
|
|
||||||
|
const (
|
||||||
|
SE_PRIVILEGE_ENABLED = 2
|
||||||
|
|
||||||
|
ERROR_NOT_ALL_ASSIGNED syscall.Errno = 1300
|
||||||
|
|
||||||
|
SeBackupPrivilege = "SeBackupPrivilege"
|
||||||
|
SeRestorePrivilege = "SeRestorePrivilege"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
securityAnonymous = iota
|
||||||
|
securityIdentification
|
||||||
|
securityImpersonation
|
||||||
|
securityDelegation
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
privNames = make(map[string]uint64)
|
||||||
|
privNameMutex sync.Mutex
|
||||||
|
)
|
||||||
|
|
||||||
|
// PrivilegeError represents an error enabling privileges.
|
||||||
|
type PrivilegeError struct {
|
||||||
|
privileges []uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *PrivilegeError) Error() string {
|
||||||
|
s := ""
|
||||||
|
if len(e.privileges) > 1 {
|
||||||
|
s = "Could not enable privileges "
|
||||||
|
} else {
|
||||||
|
s = "Could not enable privilege "
|
||||||
|
}
|
||||||
|
for i, p := range e.privileges {
|
||||||
|
if i != 0 {
|
||||||
|
s += ", "
|
||||||
|
}
|
||||||
|
s += `"`
|
||||||
|
s += getPrivilegeName(p)
|
||||||
|
s += `"`
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// RunWithPrivilege enables a single privilege for a function call.
|
||||||
|
func RunWithPrivilege(name string, fn func() error) error {
|
||||||
|
return RunWithPrivileges([]string{name}, fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RunWithPrivileges enables privileges for a function call.
|
||||||
|
func RunWithPrivileges(names []string, fn func() error) error {
|
||||||
|
privileges, err := mapPrivileges(names)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
runtime.LockOSThread()
|
||||||
|
defer runtime.UnlockOSThread()
|
||||||
|
token, err := newThreadToken()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer releaseThreadToken(token)
|
||||||
|
err = adjustPrivileges(token, privileges, SE_PRIVILEGE_ENABLED)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return fn()
|
||||||
|
}
|
||||||
|
|
||||||
|
func mapPrivileges(names []string) ([]uint64, error) {
|
||||||
|
var privileges []uint64
|
||||||
|
privNameMutex.Lock()
|
||||||
|
defer privNameMutex.Unlock()
|
||||||
|
for _, name := range names {
|
||||||
|
p, ok := privNames[name]
|
||||||
|
if !ok {
|
||||||
|
err := lookupPrivilegeValue("", name, &p)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
privNames[name] = p
|
||||||
|
}
|
||||||
|
privileges = append(privileges, p)
|
||||||
|
}
|
||||||
|
return privileges, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// EnableProcessPrivileges enables privileges globally for the process.
|
||||||
|
func EnableProcessPrivileges(names []string) error {
|
||||||
|
return enableDisableProcessPrivilege(names, SE_PRIVILEGE_ENABLED)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DisableProcessPrivileges disables privileges globally for the process.
|
||||||
|
func DisableProcessPrivileges(names []string) error {
|
||||||
|
return enableDisableProcessPrivilege(names, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func enableDisableProcessPrivilege(names []string, action uint32) error {
|
||||||
|
privileges, err := mapPrivileges(names)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
p, _ := windows.GetCurrentProcess()
|
||||||
|
var token windows.Token
|
||||||
|
err = windows.OpenProcessToken(p, windows.TOKEN_ADJUST_PRIVILEGES|windows.TOKEN_QUERY, &token)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer token.Close()
|
||||||
|
return adjustPrivileges(token, privileges, action)
|
||||||
|
}
|
||||||
|
|
||||||
|
func adjustPrivileges(token windows.Token, privileges []uint64, action uint32) error {
|
||||||
|
var b bytes.Buffer
|
||||||
|
binary.Write(&b, binary.LittleEndian, uint32(len(privileges)))
|
||||||
|
for _, p := range privileges {
|
||||||
|
binary.Write(&b, binary.LittleEndian, p)
|
||||||
|
binary.Write(&b, binary.LittleEndian, action)
|
||||||
|
}
|
||||||
|
prevState := make([]byte, b.Len())
|
||||||
|
reqSize := uint32(0)
|
||||||
|
success, err := adjustTokenPrivileges(token, false, &b.Bytes()[0], uint32(len(prevState)), &prevState[0], &reqSize)
|
||||||
|
if !success {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err == ERROR_NOT_ALL_ASSIGNED {
|
||||||
|
return &PrivilegeError{privileges}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getPrivilegeName(luid uint64) string {
|
||||||
|
var nameBuffer [256]uint16
|
||||||
|
bufSize := uint32(len(nameBuffer))
|
||||||
|
err := lookupPrivilegeName("", &luid, &nameBuffer[0], &bufSize)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Sprintf("<unknown privilege %d>", luid)
|
||||||
|
}
|
||||||
|
|
||||||
|
var displayNameBuffer [256]uint16
|
||||||
|
displayBufSize := uint32(len(displayNameBuffer))
|
||||||
|
var langID uint32
|
||||||
|
err = lookupPrivilegeDisplayName("", &nameBuffer[0], &displayNameBuffer[0], &displayBufSize, &langID)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Sprintf("<unknown privilege %s>", string(utf16.Decode(nameBuffer[:bufSize])))
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(utf16.Decode(displayNameBuffer[:displayBufSize]))
|
||||||
|
}
|
||||||
|
|
||||||
|
func newThreadToken() (windows.Token, error) {
|
||||||
|
err := impersonateSelf(securityImpersonation)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var token windows.Token
|
||||||
|
err = openThreadToken(getCurrentThread(), syscall.TOKEN_ADJUST_PRIVILEGES|syscall.TOKEN_QUERY, false, &token)
|
||||||
|
if err != nil {
|
||||||
|
rerr := revertToSelf()
|
||||||
|
if rerr != nil {
|
||||||
|
panic(rerr)
|
||||||
|
}
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return token, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func releaseThreadToken(h windows.Token) {
|
||||||
|
err := revertToSelf()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
h.Close()
|
||||||
|
}
|
128
vendor/github.com/Microsoft/go-winio/reparse.go
generated
vendored
Normal file
128
vendor/github.com/Microsoft/go-winio/reparse.go
generated
vendored
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
package winio
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"unicode/utf16"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
reparseTagMountPoint = 0xA0000003
|
||||||
|
reparseTagSymlink = 0xA000000C
|
||||||
|
)
|
||||||
|
|
||||||
|
type reparseDataBuffer struct {
|
||||||
|
ReparseTag uint32
|
||||||
|
ReparseDataLength uint16
|
||||||
|
Reserved uint16
|
||||||
|
SubstituteNameOffset uint16
|
||||||
|
SubstituteNameLength uint16
|
||||||
|
PrintNameOffset uint16
|
||||||
|
PrintNameLength uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReparsePoint describes a Win32 symlink or mount point.
|
||||||
|
type ReparsePoint struct {
|
||||||
|
Target string
|
||||||
|
IsMountPoint bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnsupportedReparsePointError is returned when trying to decode a non-symlink or
|
||||||
|
// mount point reparse point.
|
||||||
|
type UnsupportedReparsePointError struct {
|
||||||
|
Tag uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *UnsupportedReparsePointError) Error() string {
|
||||||
|
return fmt.Sprintf("unsupported reparse point %x", e.Tag)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeReparsePoint decodes a Win32 REPARSE_DATA_BUFFER structure containing either a symlink
|
||||||
|
// or a mount point.
|
||||||
|
func DecodeReparsePoint(b []byte) (*ReparsePoint, error) {
|
||||||
|
tag := binary.LittleEndian.Uint32(b[0:4])
|
||||||
|
return DecodeReparsePointData(tag, b[8:])
|
||||||
|
}
|
||||||
|
|
||||||
|
func DecodeReparsePointData(tag uint32, b []byte) (*ReparsePoint, error) {
|
||||||
|
isMountPoint := false
|
||||||
|
switch tag {
|
||||||
|
case reparseTagMountPoint:
|
||||||
|
isMountPoint = true
|
||||||
|
case reparseTagSymlink:
|
||||||
|
default:
|
||||||
|
return nil, &UnsupportedReparsePointError{tag}
|
||||||
|
}
|
||||||
|
nameOffset := 8 + binary.LittleEndian.Uint16(b[4:6])
|
||||||
|
if !isMountPoint {
|
||||||
|
nameOffset += 4
|
||||||
|
}
|
||||||
|
nameLength := binary.LittleEndian.Uint16(b[6:8])
|
||||||
|
name := make([]uint16, nameLength/2)
|
||||||
|
err := binary.Read(bytes.NewReader(b[nameOffset:nameOffset+nameLength]), binary.LittleEndian, &name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &ReparsePoint{string(utf16.Decode(name)), isMountPoint}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func isDriveLetter(c byte) bool {
|
||||||
|
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
|
||||||
|
}
|
||||||
|
|
||||||
|
// EncodeReparsePoint encodes a Win32 REPARSE_DATA_BUFFER structure describing a symlink or
|
||||||
|
// mount point.
|
||||||
|
func EncodeReparsePoint(rp *ReparsePoint) []byte {
|
||||||
|
// Generate an NT path and determine if this is a relative path.
|
||||||
|
var ntTarget string
|
||||||
|
relative := false
|
||||||
|
if strings.HasPrefix(rp.Target, `\\?\`) {
|
||||||
|
ntTarget = `\??\` + rp.Target[4:]
|
||||||
|
} else if strings.HasPrefix(rp.Target, `\\`) {
|
||||||
|
ntTarget = `\??\UNC\` + rp.Target[2:]
|
||||||
|
} else if len(rp.Target) >= 2 && isDriveLetter(rp.Target[0]) && rp.Target[1] == ':' {
|
||||||
|
ntTarget = `\??\` + rp.Target
|
||||||
|
} else {
|
||||||
|
ntTarget = rp.Target
|
||||||
|
relative = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// The paths must be NUL-terminated even though they are counted strings.
|
||||||
|
target16 := utf16.Encode([]rune(rp.Target + "\x00"))
|
||||||
|
ntTarget16 := utf16.Encode([]rune(ntTarget + "\x00"))
|
||||||
|
|
||||||
|
size := int(unsafe.Sizeof(reparseDataBuffer{})) - 8
|
||||||
|
size += len(ntTarget16)*2 + len(target16)*2
|
||||||
|
|
||||||
|
tag := uint32(reparseTagMountPoint)
|
||||||
|
if !rp.IsMountPoint {
|
||||||
|
tag = reparseTagSymlink
|
||||||
|
size += 4 // Add room for symlink flags
|
||||||
|
}
|
||||||
|
|
||||||
|
data := reparseDataBuffer{
|
||||||
|
ReparseTag: tag,
|
||||||
|
ReparseDataLength: uint16(size),
|
||||||
|
SubstituteNameOffset: 0,
|
||||||
|
SubstituteNameLength: uint16((len(ntTarget16) - 1) * 2),
|
||||||
|
PrintNameOffset: uint16(len(ntTarget16) * 2),
|
||||||
|
PrintNameLength: uint16((len(target16) - 1) * 2),
|
||||||
|
}
|
||||||
|
|
||||||
|
var b bytes.Buffer
|
||||||
|
binary.Write(&b, binary.LittleEndian, &data)
|
||||||
|
if !rp.IsMountPoint {
|
||||||
|
flags := uint32(0)
|
||||||
|
if relative {
|
||||||
|
flags |= 1
|
||||||
|
}
|
||||||
|
binary.Write(&b, binary.LittleEndian, flags)
|
||||||
|
}
|
||||||
|
|
||||||
|
binary.Write(&b, binary.LittleEndian, ntTarget16)
|
||||||
|
binary.Write(&b, binary.LittleEndian, target16)
|
||||||
|
return b.Bytes()
|
||||||
|
}
|
98
vendor/github.com/Microsoft/go-winio/sd.go
generated
vendored
Normal file
98
vendor/github.com/Microsoft/go-winio/sd.go
generated
vendored
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
// +build windows
|
||||||
|
|
||||||
|
package winio
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
//sys lookupAccountName(systemName *uint16, accountName string, sid *byte, sidSize *uint32, refDomain *uint16, refDomainSize *uint32, sidNameUse *uint32) (err error) = advapi32.LookupAccountNameW
|
||||||
|
//sys convertSidToStringSid(sid *byte, str **uint16) (err error) = advapi32.ConvertSidToStringSidW
|
||||||
|
//sys convertStringSecurityDescriptorToSecurityDescriptor(str string, revision uint32, sd *uintptr, size *uint32) (err error) = advapi32.ConvertStringSecurityDescriptorToSecurityDescriptorW
|
||||||
|
//sys convertSecurityDescriptorToStringSecurityDescriptor(sd *byte, revision uint32, secInfo uint32, sddl **uint16, sddlSize *uint32) (err error) = advapi32.ConvertSecurityDescriptorToStringSecurityDescriptorW
|
||||||
|
//sys localFree(mem uintptr) = LocalFree
|
||||||
|
//sys getSecurityDescriptorLength(sd uintptr) (len uint32) = advapi32.GetSecurityDescriptorLength
|
||||||
|
|
||||||
|
const (
|
||||||
|
cERROR_NONE_MAPPED = syscall.Errno(1332)
|
||||||
|
)
|
||||||
|
|
||||||
|
type AccountLookupError struct {
|
||||||
|
Name string
|
||||||
|
Err error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *AccountLookupError) Error() string {
|
||||||
|
if e.Name == "" {
|
||||||
|
return "lookup account: empty account name specified"
|
||||||
|
}
|
||||||
|
var s string
|
||||||
|
switch e.Err {
|
||||||
|
case cERROR_NONE_MAPPED:
|
||||||
|
s = "not found"
|
||||||
|
default:
|
||||||
|
s = e.Err.Error()
|
||||||
|
}
|
||||||
|
return "lookup account " + e.Name + ": " + s
|
||||||
|
}
|
||||||
|
|
||||||
|
type SddlConversionError struct {
|
||||||
|
Sddl string
|
||||||
|
Err error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *SddlConversionError) Error() string {
|
||||||
|
return "convert " + e.Sddl + ": " + e.Err.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
// LookupSidByName looks up the SID of an account by name
|
||||||
|
func LookupSidByName(name string) (sid string, err error) {
|
||||||
|
if name == "" {
|
||||||
|
return "", &AccountLookupError{name, cERROR_NONE_MAPPED}
|
||||||
|
}
|
||||||
|
|
||||||
|
var sidSize, sidNameUse, refDomainSize uint32
|
||||||
|
err = lookupAccountName(nil, name, nil, &sidSize, nil, &refDomainSize, &sidNameUse)
|
||||||
|
if err != nil && err != syscall.ERROR_INSUFFICIENT_BUFFER {
|
||||||
|
return "", &AccountLookupError{name, err}
|
||||||
|
}
|
||||||
|
sidBuffer := make([]byte, sidSize)
|
||||||
|
refDomainBuffer := make([]uint16, refDomainSize)
|
||||||
|
err = lookupAccountName(nil, name, &sidBuffer[0], &sidSize, &refDomainBuffer[0], &refDomainSize, &sidNameUse)
|
||||||
|
if err != nil {
|
||||||
|
return "", &AccountLookupError{name, err}
|
||||||
|
}
|
||||||
|
var strBuffer *uint16
|
||||||
|
err = convertSidToStringSid(&sidBuffer[0], &strBuffer)
|
||||||
|
if err != nil {
|
||||||
|
return "", &AccountLookupError{name, err}
|
||||||
|
}
|
||||||
|
sid = syscall.UTF16ToString((*[0xffff]uint16)(unsafe.Pointer(strBuffer))[:])
|
||||||
|
localFree(uintptr(unsafe.Pointer(strBuffer)))
|
||||||
|
return sid, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func SddlToSecurityDescriptor(sddl string) ([]byte, error) {
|
||||||
|
var sdBuffer uintptr
|
||||||
|
err := convertStringSecurityDescriptorToSecurityDescriptor(sddl, 1, &sdBuffer, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, &SddlConversionError{sddl, err}
|
||||||
|
}
|
||||||
|
defer localFree(sdBuffer)
|
||||||
|
sd := make([]byte, getSecurityDescriptorLength(sdBuffer))
|
||||||
|
copy(sd, (*[0xffff]byte)(unsafe.Pointer(sdBuffer))[:len(sd)])
|
||||||
|
return sd, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func SecurityDescriptorToSddl(sd []byte) (string, error) {
|
||||||
|
var sddl *uint16
|
||||||
|
// The returned string length seems to including an aribtrary number of terminating NULs.
|
||||||
|
// Don't use it.
|
||||||
|
err := convertSecurityDescriptorToStringSecurityDescriptor(&sd[0], 1, 0xff, &sddl, nil)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer localFree(uintptr(unsafe.Pointer(sddl)))
|
||||||
|
return syscall.UTF16ToString((*[0xffff]uint16)(unsafe.Pointer(sddl))[:]), nil
|
||||||
|
}
|
3
vendor/github.com/Microsoft/go-winio/syscall.go
generated
vendored
Normal file
3
vendor/github.com/Microsoft/go-winio/syscall.go
generated
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
package winio
|
||||||
|
|
||||||
|
//go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -output zsyscall_windows.go file.go pipe.go sd.go fileinfo.go privilege.go backup.go
|
496
vendor/github.com/Microsoft/go-winio/zsyscall_windows.go
generated
vendored
Normal file
496
vendor/github.com/Microsoft/go-winio/zsyscall_windows.go
generated
vendored
Normal file
@ -0,0 +1,496 @@
|
|||||||
|
// MACHINE GENERATED BY 'go generate' COMMAND; DO NOT EDIT
|
||||||
|
|
||||||
|
package winio
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ unsafe.Pointer
|
||||||
|
|
||||||
|
var (
|
||||||
|
modkernel32 = windows.NewLazySystemDLL("kernel32.dll")
|
||||||
|
modwinmm = windows.NewLazySystemDLL("winmm.dll")
|
||||||
|
modadvapi32 = windows.NewLazySystemDLL("advapi32.dll")
|
||||||
|
|
||||||
|
procCancelIoEx = modkernel32.NewProc("CancelIoEx")
|
||||||
|
procCreateIoCompletionPort = modkernel32.NewProc("CreateIoCompletionPort")
|
||||||
|
procGetQueuedCompletionStatus = modkernel32.NewProc("GetQueuedCompletionStatus")
|
||||||
|
procSetFileCompletionNotificationModes = modkernel32.NewProc("SetFileCompletionNotificationModes")
|
||||||
|
proctimeBeginPeriod = modwinmm.NewProc("timeBeginPeriod")
|
||||||
|
procConnectNamedPipe = modkernel32.NewProc("ConnectNamedPipe")
|
||||||
|
procCreateNamedPipeW = modkernel32.NewProc("CreateNamedPipeW")
|
||||||
|
procCreateFileW = modkernel32.NewProc("CreateFileW")
|
||||||
|
procWaitNamedPipeW = modkernel32.NewProc("WaitNamedPipeW")
|
||||||
|
procGetNamedPipeInfo = modkernel32.NewProc("GetNamedPipeInfo")
|
||||||
|
procGetNamedPipeHandleStateW = modkernel32.NewProc("GetNamedPipeHandleStateW")
|
||||||
|
procLookupAccountNameW = modadvapi32.NewProc("LookupAccountNameW")
|
||||||
|
procConvertSidToStringSidW = modadvapi32.NewProc("ConvertSidToStringSidW")
|
||||||
|
procConvertStringSecurityDescriptorToSecurityDescriptorW = modadvapi32.NewProc("ConvertStringSecurityDescriptorToSecurityDescriptorW")
|
||||||
|
procConvertSecurityDescriptorToStringSecurityDescriptorW = modadvapi32.NewProc("ConvertSecurityDescriptorToStringSecurityDescriptorW")
|
||||||
|
procLocalFree = modkernel32.NewProc("LocalFree")
|
||||||
|
procGetSecurityDescriptorLength = modadvapi32.NewProc("GetSecurityDescriptorLength")
|
||||||
|
procGetFileInformationByHandleEx = modkernel32.NewProc("GetFileInformationByHandleEx")
|
||||||
|
procSetFileInformationByHandle = modkernel32.NewProc("SetFileInformationByHandle")
|
||||||
|
procAdjustTokenPrivileges = modadvapi32.NewProc("AdjustTokenPrivileges")
|
||||||
|
procImpersonateSelf = modadvapi32.NewProc("ImpersonateSelf")
|
||||||
|
procRevertToSelf = modadvapi32.NewProc("RevertToSelf")
|
||||||
|
procOpenThreadToken = modadvapi32.NewProc("OpenThreadToken")
|
||||||
|
procGetCurrentThread = modkernel32.NewProc("GetCurrentThread")
|
||||||
|
procLookupPrivilegeValueW = modadvapi32.NewProc("LookupPrivilegeValueW")
|
||||||
|
procLookupPrivilegeNameW = modadvapi32.NewProc("LookupPrivilegeNameW")
|
||||||
|
procLookupPrivilegeDisplayNameW = modadvapi32.NewProc("LookupPrivilegeDisplayNameW")
|
||||||
|
procBackupRead = modkernel32.NewProc("BackupRead")
|
||||||
|
procBackupWrite = modkernel32.NewProc("BackupWrite")
|
||||||
|
)
|
||||||
|
|
||||||
|
func cancelIoEx(file syscall.Handle, o *syscall.Overlapped) (err error) {
|
||||||
|
r1, _, e1 := syscall.Syscall(procCancelIoEx.Addr(), 2, uintptr(file), uintptr(unsafe.Pointer(o)), 0)
|
||||||
|
if r1 == 0 {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = error(e1)
|
||||||
|
} else {
|
||||||
|
err = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func createIoCompletionPort(file syscall.Handle, port syscall.Handle, key uintptr, threadCount uint32) (newport syscall.Handle, err error) {
|
||||||
|
r0, _, e1 := syscall.Syscall6(procCreateIoCompletionPort.Addr(), 4, uintptr(file), uintptr(port), uintptr(key), uintptr(threadCount), 0, 0)
|
||||||
|
newport = syscall.Handle(r0)
|
||||||
|
if newport == 0 {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = error(e1)
|
||||||
|
} else {
|
||||||
|
err = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func getQueuedCompletionStatus(port syscall.Handle, bytes *uint32, key *uintptr, o **ioOperation, timeout uint32) (err error) {
|
||||||
|
r1, _, e1 := syscall.Syscall6(procGetQueuedCompletionStatus.Addr(), 5, uintptr(port), uintptr(unsafe.Pointer(bytes)), uintptr(unsafe.Pointer(key)), uintptr(unsafe.Pointer(o)), uintptr(timeout), 0)
|
||||||
|
if r1 == 0 {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = error(e1)
|
||||||
|
} else {
|
||||||
|
err = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func setFileCompletionNotificationModes(h syscall.Handle, flags uint8) (err error) {
|
||||||
|
r1, _, e1 := syscall.Syscall(procSetFileCompletionNotificationModes.Addr(), 2, uintptr(h), uintptr(flags), 0)
|
||||||
|
if r1 == 0 {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = error(e1)
|
||||||
|
} else {
|
||||||
|
err = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func timeBeginPeriod(period uint32) (n int32) {
|
||||||
|
r0, _, _ := syscall.Syscall(proctimeBeginPeriod.Addr(), 1, uintptr(period), 0, 0)
|
||||||
|
n = int32(r0)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func connectNamedPipe(pipe syscall.Handle, o *syscall.Overlapped) (err error) {
|
||||||
|
r1, _, e1 := syscall.Syscall(procConnectNamedPipe.Addr(), 2, uintptr(pipe), uintptr(unsafe.Pointer(o)), 0)
|
||||||
|
if r1 == 0 {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = error(e1)
|
||||||
|
} else {
|
||||||
|
err = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func createNamedPipe(name string, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *securityAttributes) (handle syscall.Handle, err error) {
|
||||||
|
var _p0 *uint16
|
||||||
|
_p0, err = syscall.UTF16PtrFromString(name)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return _createNamedPipe(_p0, flags, pipeMode, maxInstances, outSize, inSize, defaultTimeout, sa)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _createNamedPipe(name *uint16, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *securityAttributes) (handle syscall.Handle, err error) {
|
||||||
|
r0, _, e1 := syscall.Syscall9(procCreateNamedPipeW.Addr(), 8, uintptr(unsafe.Pointer(name)), uintptr(flags), uintptr(pipeMode), uintptr(maxInstances), uintptr(outSize), uintptr(inSize), uintptr(defaultTimeout), uintptr(unsafe.Pointer(sa)), 0)
|
||||||
|
handle = syscall.Handle(r0)
|
||||||
|
if handle == syscall.InvalidHandle {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = error(e1)
|
||||||
|
} else {
|
||||||
|
err = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func createFile(name string, access uint32, mode uint32, sa *securityAttributes, createmode uint32, attrs uint32, templatefile syscall.Handle) (handle syscall.Handle, err error) {
|
||||||
|
var _p0 *uint16
|
||||||
|
_p0, err = syscall.UTF16PtrFromString(name)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return _createFile(_p0, access, mode, sa, createmode, attrs, templatefile)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _createFile(name *uint16, access uint32, mode uint32, sa *securityAttributes, createmode uint32, attrs uint32, templatefile syscall.Handle) (handle syscall.Handle, err error) {
|
||||||
|
r0, _, e1 := syscall.Syscall9(procCreateFileW.Addr(), 7, uintptr(unsafe.Pointer(name)), uintptr(access), uintptr(mode), uintptr(unsafe.Pointer(sa)), uintptr(createmode), uintptr(attrs), uintptr(templatefile), 0, 0)
|
||||||
|
handle = syscall.Handle(r0)
|
||||||
|
if handle == syscall.InvalidHandle {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = error(e1)
|
||||||
|
} else {
|
||||||
|
err = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func waitNamedPipe(name string, timeout uint32) (err error) {
|
||||||
|
var _p0 *uint16
|
||||||
|
_p0, err = syscall.UTF16PtrFromString(name)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return _waitNamedPipe(_p0, timeout)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _waitNamedPipe(name *uint16, timeout uint32) (err error) {
|
||||||
|
r1, _, e1 := syscall.Syscall(procWaitNamedPipeW.Addr(), 2, uintptr(unsafe.Pointer(name)), uintptr(timeout), 0)
|
||||||
|
if r1 == 0 {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = error(e1)
|
||||||
|
} else {
|
||||||
|
err = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func getNamedPipeInfo(pipe syscall.Handle, flags *uint32, outSize *uint32, inSize *uint32, maxInstances *uint32) (err error) {
|
||||||
|
r1, _, e1 := syscall.Syscall6(procGetNamedPipeInfo.Addr(), 5, uintptr(pipe), uintptr(unsafe.Pointer(flags)), uintptr(unsafe.Pointer(outSize)), uintptr(unsafe.Pointer(inSize)), uintptr(unsafe.Pointer(maxInstances)), 0)
|
||||||
|
if r1 == 0 {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = error(e1)
|
||||||
|
} else {
|
||||||
|
err = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func getNamedPipeHandleState(pipe syscall.Handle, state *uint32, curInstances *uint32, maxCollectionCount *uint32, collectDataTimeout *uint32, userName *uint16, maxUserNameSize uint32) (err error) {
|
||||||
|
r1, _, e1 := syscall.Syscall9(procGetNamedPipeHandleStateW.Addr(), 7, uintptr(pipe), uintptr(unsafe.Pointer(state)), uintptr(unsafe.Pointer(curInstances)), uintptr(unsafe.Pointer(maxCollectionCount)), uintptr(unsafe.Pointer(collectDataTimeout)), uintptr(unsafe.Pointer(userName)), uintptr(maxUserNameSize), 0, 0)
|
||||||
|
if r1 == 0 {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = error(e1)
|
||||||
|
} else {
|
||||||
|
err = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func lookupAccountName(systemName *uint16, accountName string, sid *byte, sidSize *uint32, refDomain *uint16, refDomainSize *uint32, sidNameUse *uint32) (err error) {
|
||||||
|
var _p0 *uint16
|
||||||
|
_p0, err = syscall.UTF16PtrFromString(accountName)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return _lookupAccountName(systemName, _p0, sid, sidSize, refDomain, refDomainSize, sidNameUse)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _lookupAccountName(systemName *uint16, accountName *uint16, sid *byte, sidSize *uint32, refDomain *uint16, refDomainSize *uint32, sidNameUse *uint32) (err error) {
|
||||||
|
r1, _, e1 := syscall.Syscall9(procLookupAccountNameW.Addr(), 7, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(accountName)), uintptr(unsafe.Pointer(sid)), uintptr(unsafe.Pointer(sidSize)), uintptr(unsafe.Pointer(refDomain)), uintptr(unsafe.Pointer(refDomainSize)), uintptr(unsafe.Pointer(sidNameUse)), 0, 0)
|
||||||
|
if r1 == 0 {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = error(e1)
|
||||||
|
} else {
|
||||||
|
err = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func convertSidToStringSid(sid *byte, str **uint16) (err error) {
|
||||||
|
r1, _, e1 := syscall.Syscall(procConvertSidToStringSidW.Addr(), 2, uintptr(unsafe.Pointer(sid)), uintptr(unsafe.Pointer(str)), 0)
|
||||||
|
if r1 == 0 {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = error(e1)
|
||||||
|
} else {
|
||||||
|
err = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func convertStringSecurityDescriptorToSecurityDescriptor(str string, revision uint32, sd *uintptr, size *uint32) (err error) {
|
||||||
|
var _p0 *uint16
|
||||||
|
_p0, err = syscall.UTF16PtrFromString(str)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return _convertStringSecurityDescriptorToSecurityDescriptor(_p0, revision, sd, size)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _convertStringSecurityDescriptorToSecurityDescriptor(str *uint16, revision uint32, sd *uintptr, size *uint32) (err error) {
|
||||||
|
r1, _, e1 := syscall.Syscall6(procConvertStringSecurityDescriptorToSecurityDescriptorW.Addr(), 4, uintptr(unsafe.Pointer(str)), uintptr(revision), uintptr(unsafe.Pointer(sd)), uintptr(unsafe.Pointer(size)), 0, 0)
|
||||||
|
if r1 == 0 {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = error(e1)
|
||||||
|
} else {
|
||||||
|
err = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func convertSecurityDescriptorToStringSecurityDescriptor(sd *byte, revision uint32, secInfo uint32, sddl **uint16, sddlSize *uint32) (err error) {
|
||||||
|
r1, _, e1 := syscall.Syscall6(procConvertSecurityDescriptorToStringSecurityDescriptorW.Addr(), 5, uintptr(unsafe.Pointer(sd)), uintptr(revision), uintptr(secInfo), uintptr(unsafe.Pointer(sddl)), uintptr(unsafe.Pointer(sddlSize)), 0)
|
||||||
|
if r1 == 0 {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = error(e1)
|
||||||
|
} else {
|
||||||
|
err = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func localFree(mem uintptr) {
|
||||||
|
syscall.Syscall(procLocalFree.Addr(), 1, uintptr(mem), 0, 0)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func getSecurityDescriptorLength(sd uintptr) (len uint32) {
|
||||||
|
r0, _, _ := syscall.Syscall(procGetSecurityDescriptorLength.Addr(), 1, uintptr(sd), 0, 0)
|
||||||
|
len = uint32(r0)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func getFileInformationByHandleEx(h syscall.Handle, class uint32, buffer *byte, size uint32) (err error) {
|
||||||
|
r1, _, e1 := syscall.Syscall6(procGetFileInformationByHandleEx.Addr(), 4, uintptr(h), uintptr(class), uintptr(unsafe.Pointer(buffer)), uintptr(size), 0, 0)
|
||||||
|
if r1 == 0 {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = error(e1)
|
||||||
|
} else {
|
||||||
|
err = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func setFileInformationByHandle(h syscall.Handle, class uint32, buffer *byte, size uint32) (err error) {
|
||||||
|
r1, _, e1 := syscall.Syscall6(procSetFileInformationByHandle.Addr(), 4, uintptr(h), uintptr(class), uintptr(unsafe.Pointer(buffer)), uintptr(size), 0, 0)
|
||||||
|
if r1 == 0 {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = error(e1)
|
||||||
|
} else {
|
||||||
|
err = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func adjustTokenPrivileges(token windows.Token, releaseAll bool, input *byte, outputSize uint32, output *byte, requiredSize *uint32) (success bool, err error) {
|
||||||
|
var _p0 uint32
|
||||||
|
if releaseAll {
|
||||||
|
_p0 = 1
|
||||||
|
} else {
|
||||||
|
_p0 = 0
|
||||||
|
}
|
||||||
|
r0, _, e1 := syscall.Syscall6(procAdjustTokenPrivileges.Addr(), 6, uintptr(token), uintptr(_p0), uintptr(unsafe.Pointer(input)), uintptr(outputSize), uintptr(unsafe.Pointer(output)), uintptr(unsafe.Pointer(requiredSize)))
|
||||||
|
success = r0 != 0
|
||||||
|
if true {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = error(e1)
|
||||||
|
} else {
|
||||||
|
err = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func impersonateSelf(level uint32) (err error) {
|
||||||
|
r1, _, e1 := syscall.Syscall(procImpersonateSelf.Addr(), 1, uintptr(level), 0, 0)
|
||||||
|
if r1 == 0 {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = error(e1)
|
||||||
|
} else {
|
||||||
|
err = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func revertToSelf() (err error) {
|
||||||
|
r1, _, e1 := syscall.Syscall(procRevertToSelf.Addr(), 0, 0, 0, 0)
|
||||||
|
if r1 == 0 {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = error(e1)
|
||||||
|
} else {
|
||||||
|
err = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func openThreadToken(thread syscall.Handle, accessMask uint32, openAsSelf bool, token *windows.Token) (err error) {
|
||||||
|
var _p0 uint32
|
||||||
|
if openAsSelf {
|
||||||
|
_p0 = 1
|
||||||
|
} else {
|
||||||
|
_p0 = 0
|
||||||
|
}
|
||||||
|
r1, _, e1 := syscall.Syscall6(procOpenThreadToken.Addr(), 4, uintptr(thread), uintptr(accessMask), uintptr(_p0), uintptr(unsafe.Pointer(token)), 0, 0)
|
||||||
|
if r1 == 0 {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = error(e1)
|
||||||
|
} else {
|
||||||
|
err = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func getCurrentThread() (h syscall.Handle) {
|
||||||
|
r0, _, _ := syscall.Syscall(procGetCurrentThread.Addr(), 0, 0, 0, 0)
|
||||||
|
h = syscall.Handle(r0)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func lookupPrivilegeValue(systemName string, name string, luid *uint64) (err error) {
|
||||||
|
var _p0 *uint16
|
||||||
|
_p0, err = syscall.UTF16PtrFromString(systemName)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var _p1 *uint16
|
||||||
|
_p1, err = syscall.UTF16PtrFromString(name)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return _lookupPrivilegeValue(_p0, _p1, luid)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _lookupPrivilegeValue(systemName *uint16, name *uint16, luid *uint64) (err error) {
|
||||||
|
r1, _, e1 := syscall.Syscall(procLookupPrivilegeValueW.Addr(), 3, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(luid)))
|
||||||
|
if r1 == 0 {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = error(e1)
|
||||||
|
} else {
|
||||||
|
err = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func lookupPrivilegeName(systemName string, luid *uint64, buffer *uint16, size *uint32) (err error) {
|
||||||
|
var _p0 *uint16
|
||||||
|
_p0, err = syscall.UTF16PtrFromString(systemName)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return _lookupPrivilegeName(_p0, luid, buffer, size)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _lookupPrivilegeName(systemName *uint16, luid *uint64, buffer *uint16, size *uint32) (err error) {
|
||||||
|
r1, _, e1 := syscall.Syscall6(procLookupPrivilegeNameW.Addr(), 4, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(luid)), uintptr(unsafe.Pointer(buffer)), uintptr(unsafe.Pointer(size)), 0, 0)
|
||||||
|
if r1 == 0 {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = error(e1)
|
||||||
|
} else {
|
||||||
|
err = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func lookupPrivilegeDisplayName(systemName string, name *uint16, buffer *uint16, size *uint32, languageId *uint32) (err error) {
|
||||||
|
var _p0 *uint16
|
||||||
|
_p0, err = syscall.UTF16PtrFromString(systemName)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return _lookupPrivilegeDisplayName(_p0, name, buffer, size, languageId)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _lookupPrivilegeDisplayName(systemName *uint16, name *uint16, buffer *uint16, size *uint32, languageId *uint32) (err error) {
|
||||||
|
r1, _, e1 := syscall.Syscall6(procLookupPrivilegeDisplayNameW.Addr(), 5, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(buffer)), uintptr(unsafe.Pointer(size)), uintptr(unsafe.Pointer(languageId)), 0)
|
||||||
|
if r1 == 0 {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = error(e1)
|
||||||
|
} else {
|
||||||
|
err = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func backupRead(h syscall.Handle, b []byte, bytesRead *uint32, abort bool, processSecurity bool, context *uintptr) (err error) {
|
||||||
|
var _p0 *byte
|
||||||
|
if len(b) > 0 {
|
||||||
|
_p0 = &b[0]
|
||||||
|
}
|
||||||
|
var _p1 uint32
|
||||||
|
if abort {
|
||||||
|
_p1 = 1
|
||||||
|
} else {
|
||||||
|
_p1 = 0
|
||||||
|
}
|
||||||
|
var _p2 uint32
|
||||||
|
if processSecurity {
|
||||||
|
_p2 = 1
|
||||||
|
} else {
|
||||||
|
_p2 = 0
|
||||||
|
}
|
||||||
|
r1, _, e1 := syscall.Syscall9(procBackupRead.Addr(), 7, uintptr(h), uintptr(unsafe.Pointer(_p0)), uintptr(len(b)), uintptr(unsafe.Pointer(bytesRead)), uintptr(_p1), uintptr(_p2), uintptr(unsafe.Pointer(context)), 0, 0)
|
||||||
|
if r1 == 0 {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = error(e1)
|
||||||
|
} else {
|
||||||
|
err = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func backupWrite(h syscall.Handle, b []byte, bytesWritten *uint32, abort bool, processSecurity bool, context *uintptr) (err error) {
|
||||||
|
var _p0 *byte
|
||||||
|
if len(b) > 0 {
|
||||||
|
_p0 = &b[0]
|
||||||
|
}
|
||||||
|
var _p1 uint32
|
||||||
|
if abort {
|
||||||
|
_p1 = 1
|
||||||
|
} else {
|
||||||
|
_p1 = 0
|
||||||
|
}
|
||||||
|
var _p2 uint32
|
||||||
|
if processSecurity {
|
||||||
|
_p2 = 1
|
||||||
|
} else {
|
||||||
|
_p2 = 0
|
||||||
|
}
|
||||||
|
r1, _, e1 := syscall.Syscall9(procBackupWrite.Addr(), 7, uintptr(h), uintptr(unsafe.Pointer(_p0)), uintptr(len(b)), uintptr(unsafe.Pointer(bytesWritten)), uintptr(_p1), uintptr(_p2), uintptr(unsafe.Pointer(context)), 0, 0)
|
||||||
|
if r1 == 0 {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = error(e1)
|
||||||
|
} else {
|
||||||
|
err = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
29
vendor/github.com/cncd/logging/LICENSE
generated
vendored
Normal file
29
vendor/github.com/cncd/logging/LICENSE
generated
vendored
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
BSD 3-Clause License
|
||||||
|
|
||||||
|
Copyright (c) 2017, Brad Rydzewski
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright notice, this
|
||||||
|
list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
* Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation
|
||||||
|
and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
* Neither the name of the copyright holder nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived from
|
||||||
|
this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
6
vendor/github.com/cncd/logging/README
generated
vendored
Normal file
6
vendor/github.com/cncd/logging/README
generated
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
Go package provides a common interface for storing and streaming logs.
|
||||||
|
|
||||||
|
Documentation:
|
||||||
|
|
||||||
|
http://godoc.org/github.com/cncd/logging
|
||||||
|
http://godoc.org/github.com/cncd/logging/gcp
|
143
vendor/github.com/cncd/logging/log.go
generated
vendored
Normal file
143
vendor/github.com/cncd/logging/log.go
generated
vendored
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
package logging
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"io"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TODO (bradrydzewski) writing to subscribers is currently a blocking
|
||||||
|
// operation and does not protect against slow clients from locking
|
||||||
|
// the stream. This should be resolved.
|
||||||
|
|
||||||
|
// TODO (bradrydzewski) implement a mux.Info to fetch information and
|
||||||
|
// statistics for the multiplexier. Streams, subscribers, etc
|
||||||
|
// mux.Info()
|
||||||
|
|
||||||
|
// TODO (bradrydzewski) refactor code to place publisher and subscriber
|
||||||
|
// operations in separate files with more encapsulated logic.
|
||||||
|
// sub.push()
|
||||||
|
// sub.join()
|
||||||
|
// sub.start()... event loop
|
||||||
|
|
||||||
|
type subscriber struct {
|
||||||
|
handler Handler
|
||||||
|
}
|
||||||
|
|
||||||
|
type stream struct {
|
||||||
|
sync.Mutex
|
||||||
|
|
||||||
|
path string
|
||||||
|
hist []*Entry
|
||||||
|
subs map[*subscriber]struct{}
|
||||||
|
done chan struct{}
|
||||||
|
wait sync.WaitGroup
|
||||||
|
}
|
||||||
|
|
||||||
|
type log struct {
|
||||||
|
sync.Mutex
|
||||||
|
|
||||||
|
streams map[string]*stream
|
||||||
|
}
|
||||||
|
|
||||||
|
// New returns a new logger.
|
||||||
|
func New() Log {
|
||||||
|
return &log{
|
||||||
|
streams: map[string]*stream{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *log) Open(c context.Context, path string) error {
|
||||||
|
l.Lock()
|
||||||
|
_, ok := l.streams[path]
|
||||||
|
if !ok {
|
||||||
|
l.streams[path] = &stream{
|
||||||
|
path: path,
|
||||||
|
subs: make(map[*subscriber]struct{}),
|
||||||
|
done: make(chan struct{}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
l.Unlock()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *log) Write(c context.Context, path string, entry *Entry) error {
|
||||||
|
l.Lock()
|
||||||
|
s, ok := l.streams[path]
|
||||||
|
l.Unlock()
|
||||||
|
if !ok {
|
||||||
|
return ErrNotFound
|
||||||
|
}
|
||||||
|
s.Lock()
|
||||||
|
s.hist = append(s.hist, entry)
|
||||||
|
for sub := range s.subs {
|
||||||
|
go sub.handler(entry)
|
||||||
|
}
|
||||||
|
s.Unlock()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *log) Tail(c context.Context, path string, handler Handler) error {
|
||||||
|
l.Lock()
|
||||||
|
s, ok := l.streams[path]
|
||||||
|
l.Unlock()
|
||||||
|
if !ok {
|
||||||
|
return ErrNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
sub := &subscriber{
|
||||||
|
handler: handler,
|
||||||
|
}
|
||||||
|
s.Lock()
|
||||||
|
if len(s.hist) != 0 {
|
||||||
|
sub.handler(s.hist...)
|
||||||
|
}
|
||||||
|
s.subs[sub] = struct{}{}
|
||||||
|
s.Unlock()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-c.Done():
|
||||||
|
case <-s.done:
|
||||||
|
}
|
||||||
|
|
||||||
|
s.Lock()
|
||||||
|
delete(s.subs, sub)
|
||||||
|
s.Unlock()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *log) Close(c context.Context, path string) error {
|
||||||
|
l.Lock()
|
||||||
|
s, ok := l.streams[path]
|
||||||
|
l.Unlock()
|
||||||
|
if !ok {
|
||||||
|
return ErrNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
s.Lock()
|
||||||
|
close(s.done)
|
||||||
|
s.Unlock()
|
||||||
|
|
||||||
|
l.Lock()
|
||||||
|
delete(l.streams, path)
|
||||||
|
l.Unlock()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *log) Snapshot(c context.Context, path string, w io.Writer) error {
|
||||||
|
l.Lock()
|
||||||
|
s, ok := l.streams[path]
|
||||||
|
l.Unlock()
|
||||||
|
if !ok {
|
||||||
|
return ErrNotFound
|
||||||
|
}
|
||||||
|
s.Lock()
|
||||||
|
for _, entry := range s.hist {
|
||||||
|
w.Write(entry.Data)
|
||||||
|
w.Write(cr)
|
||||||
|
}
|
||||||
|
s.Unlock()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var cr = []byte{'\n'}
|
80
vendor/github.com/cncd/logging/logging.go
generated
vendored
Normal file
80
vendor/github.com/cncd/logging/logging.go
generated
vendored
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
package logging
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ErrNotFound is returned when the log does not exist.
|
||||||
|
var ErrNotFound = errors.New("stream: not found")
|
||||||
|
|
||||||
|
// Entry defines a log entry.
|
||||||
|
type Entry struct {
|
||||||
|
// ID identifies this message.
|
||||||
|
ID string `json:"id,omitempty"`
|
||||||
|
|
||||||
|
// Data is the actual data in the entry.
|
||||||
|
Data []byte `json:"data"`
|
||||||
|
|
||||||
|
// Tags represents the key-value pairs the
|
||||||
|
// entry is tagged with.
|
||||||
|
Tags map[string]string `json:"tags,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handler defines a callback function for handling log entries.
|
||||||
|
type Handler func(...*Entry)
|
||||||
|
|
||||||
|
// Log defines a log multiplexer.
|
||||||
|
type Log interface {
|
||||||
|
// Open opens the log.
|
||||||
|
Open(c context.Context, path string) error
|
||||||
|
|
||||||
|
// Write writes the entry to the log.
|
||||||
|
Write(c context.Context, path string, entry *Entry) error
|
||||||
|
|
||||||
|
// Tail tails the log.
|
||||||
|
Tail(c context.Context, path string, handler Handler) error
|
||||||
|
|
||||||
|
// Close closes the log.
|
||||||
|
Close(c context.Context, path string) error
|
||||||
|
|
||||||
|
// Snapshot snapshots the stream to Writer w.
|
||||||
|
Snapshot(c context.Context, path string, w io.Writer) error
|
||||||
|
|
||||||
|
// Info returns runtime information about the multiplexer.
|
||||||
|
// Info(c context.Context) (interface{}, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// // global streamer
|
||||||
|
// var global = New()
|
||||||
|
//
|
||||||
|
// // Set sets a default global logger.
|
||||||
|
// func Set(log Log) {
|
||||||
|
// global = log
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // Open opens the log stream.
|
||||||
|
// func Open(c context.Context, path string) error {
|
||||||
|
// return global.Open(c, path)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // Write writes the log entry to the stream.
|
||||||
|
// func Write(c context.Context, path string, entry *Entry) error {
|
||||||
|
// return global.Write(c, path, entry)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // Tail tails the log stream.
|
||||||
|
// func Tail(c context.Context, path string, handler Handler) error {
|
||||||
|
// return global.Tail(c, path, handler)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // Close closes the log stream.
|
||||||
|
// func Close(c context.Context, path string) error {
|
||||||
|
// return global.Close(c, path)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // Snapshot snapshots the stream to Writer w.
|
||||||
|
// func Snapshot(c context.Context, path string, w io.Writer) error {
|
||||||
|
// return global.Snapshot(c, path, w)
|
||||||
|
// }
|
29
vendor/github.com/cncd/pipeline/LICENSE
generated
vendored
Normal file
29
vendor/github.com/cncd/pipeline/LICENSE
generated
vendored
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
BSD 3-Clause License
|
||||||
|
|
||||||
|
Copyright (c) 2017, Brad Rydzewski
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright notice, this
|
||||||
|
list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
* Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation
|
||||||
|
and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
* Neither the name of the copyright holder nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived from
|
||||||
|
this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
21
vendor/github.com/cncd/pipeline/pipeline/backend/backend.go
generated
vendored
Normal file
21
vendor/github.com/cncd/pipeline/pipeline/backend/backend.go
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
package backend
|
||||||
|
|
||||||
|
import "io"
|
||||||
|
|
||||||
|
// Engine defines a container orchestration backend and is used
|
||||||
|
// to create and manage container resources.
|
||||||
|
type Engine interface {
|
||||||
|
// Setup the pipeline environment.
|
||||||
|
Setup(*Config) error
|
||||||
|
// Start the pipeline step.
|
||||||
|
Exec(*Step) error
|
||||||
|
// Kill the pipeline step.
|
||||||
|
Kill(*Step) error
|
||||||
|
// Wait for the pipeline step to complete and returns
|
||||||
|
// the completion results.
|
||||||
|
Wait(*Step) (*State, error)
|
||||||
|
// Tail the pipeline step logs.
|
||||||
|
Tail(*Step) (io.ReadCloser, error)
|
||||||
|
// Destroy the pipeline environment.
|
||||||
|
Destroy(*Config) error
|
||||||
|
}
|
130
vendor/github.com/cncd/pipeline/pipeline/backend/docker/convert.go
generated
vendored
Normal file
130
vendor/github.com/cncd/pipeline/pipeline/backend/docker/convert.go
generated
vendored
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
package docker
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/json"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/cncd/pipeline/pipeline/backend"
|
||||||
|
|
||||||
|
"github.com/docker/docker/api/types/container"
|
||||||
|
)
|
||||||
|
|
||||||
|
// returns a container configuration.
|
||||||
|
func toConfig(proc *backend.Step) *container.Config {
|
||||||
|
config := &container.Config{
|
||||||
|
Image: proc.Image,
|
||||||
|
Labels: proc.Labels,
|
||||||
|
WorkingDir: proc.WorkingDir,
|
||||||
|
AttachStdout: true,
|
||||||
|
AttachStderr: true,
|
||||||
|
}
|
||||||
|
if len(proc.Environment) != 0 {
|
||||||
|
config.Env = toEnv(proc.Environment)
|
||||||
|
}
|
||||||
|
if len(proc.Command) != 0 {
|
||||||
|
config.Cmd = proc.Command
|
||||||
|
}
|
||||||
|
if len(proc.Entrypoint) != 0 {
|
||||||
|
config.Entrypoint = proc.Entrypoint
|
||||||
|
}
|
||||||
|
if len(proc.Volumes) != 0 {
|
||||||
|
config.Volumes = toVol(proc.Volumes)
|
||||||
|
}
|
||||||
|
return config
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns a container host configuration.
|
||||||
|
func toHostConfig(proc *backend.Step) *container.HostConfig {
|
||||||
|
config := &container.HostConfig{
|
||||||
|
Resources: container.Resources{
|
||||||
|
CPUQuota: proc.CPUQuota,
|
||||||
|
CPUShares: proc.CPUShares,
|
||||||
|
CpusetCpus: proc.CPUSet,
|
||||||
|
Memory: proc.MemLimit,
|
||||||
|
MemorySwap: proc.MemSwapLimit,
|
||||||
|
},
|
||||||
|
Privileged: proc.Privileged,
|
||||||
|
ShmSize: proc.ShmSize,
|
||||||
|
}
|
||||||
|
// if len(proc.VolumesFrom) != 0 {
|
||||||
|
// config.VolumesFrom = proc.VolumesFrom
|
||||||
|
// }
|
||||||
|
// if len(proc.Network) != 0 {
|
||||||
|
// config.NetworkMode = container.NetworkMode(
|
||||||
|
// proc.Network,
|
||||||
|
// )
|
||||||
|
// }
|
||||||
|
if len(proc.DNS) != 0 {
|
||||||
|
config.DNS = proc.DNS
|
||||||
|
}
|
||||||
|
if len(proc.DNSSearch) != 0 {
|
||||||
|
config.DNSSearch = proc.DNSSearch
|
||||||
|
}
|
||||||
|
if len(proc.ExtraHosts) != 0 {
|
||||||
|
config.ExtraHosts = proc.ExtraHosts
|
||||||
|
}
|
||||||
|
if len(proc.Devices) != 0 {
|
||||||
|
config.Devices = toDev(proc.Devices)
|
||||||
|
}
|
||||||
|
if len(proc.Volumes) != 0 {
|
||||||
|
config.Binds = proc.Volumes
|
||||||
|
}
|
||||||
|
// if proc.OomKillDisable {
|
||||||
|
// config.OomKillDisable = &proc.OomKillDisable
|
||||||
|
// }
|
||||||
|
|
||||||
|
return config
|
||||||
|
}
|
||||||
|
|
||||||
|
// helper function that converts a slice of volume paths to a set of
|
||||||
|
// unique volume names.
|
||||||
|
func toVol(paths []string) map[string]struct{} {
|
||||||
|
set := map[string]struct{}{}
|
||||||
|
for _, path := range paths {
|
||||||
|
parts := strings.Split(path, ":")
|
||||||
|
if len(parts) < 2 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
set[parts[1]] = struct{}{}
|
||||||
|
}
|
||||||
|
return set
|
||||||
|
}
|
||||||
|
|
||||||
|
// helper function that converts a key value map of environment variables to a
|
||||||
|
// string slice in key=value format.
|
||||||
|
func toEnv(env map[string]string) []string {
|
||||||
|
var envs []string
|
||||||
|
for k, v := range env {
|
||||||
|
envs = append(envs, k+"="+v)
|
||||||
|
}
|
||||||
|
return envs
|
||||||
|
}
|
||||||
|
|
||||||
|
// helper function that converts a slice of device paths to a slice of
|
||||||
|
// container.DeviceMapping.
|
||||||
|
func toDev(paths []string) []container.DeviceMapping {
|
||||||
|
var devices []container.DeviceMapping
|
||||||
|
for _, path := range paths {
|
||||||
|
parts := strings.Split(path, ":")
|
||||||
|
if len(parts) < 2 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
devices = append(devices, container.DeviceMapping{
|
||||||
|
PathOnHost: parts[0],
|
||||||
|
PathInContainer: parts[1],
|
||||||
|
CgroupPermissions: "rwm",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return devices
|
||||||
|
}
|
||||||
|
|
||||||
|
// helper function that serializes the auth configuration as JSON
|
||||||
|
// base64 payload.
|
||||||
|
func encodeAuthToBase64(authConfig backend.Auth) (string, error) {
|
||||||
|
buf, err := json.Marshal(authConfig)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return base64.URLEncoding.EncodeToString(buf), nil
|
||||||
|
}
|
202
vendor/github.com/cncd/pipeline/pipeline/backend/docker/docker.go
generated
vendored
Normal file
202
vendor/github.com/cncd/pipeline/pipeline/backend/docker/docker.go
generated
vendored
Normal file
@ -0,0 +1,202 @@
|
|||||||
|
package docker
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
|
||||||
|
"github.com/cncd/pipeline/pipeline/backend"
|
||||||
|
|
||||||
|
"github.com/docker/docker/api/types"
|
||||||
|
"github.com/docker/docker/api/types/network"
|
||||||
|
"github.com/docker/docker/api/types/volume"
|
||||||
|
"github.com/docker/docker/client"
|
||||||
|
"github.com/docker/docker/pkg/stdcopy"
|
||||||
|
)
|
||||||
|
|
||||||
|
type engine struct {
|
||||||
|
client client.APIClient
|
||||||
|
}
|
||||||
|
|
||||||
|
// New returns a new Docker Engine using the given client.
|
||||||
|
func New(cli client.APIClient) backend.Engine {
|
||||||
|
return &engine{
|
||||||
|
client: cli,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewEnv returns a new Docker Engine using the client connection
|
||||||
|
// environment variables.
|
||||||
|
func NewEnv() (backend.Engine, error) {
|
||||||
|
cli, err := client.NewEnvClient()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return New(cli), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *engine) Setup(conf *backend.Config) error {
|
||||||
|
for _, vol := range conf.Volumes {
|
||||||
|
_, err := e.client.VolumeCreate(noContext, volume.VolumesCreateBody{
|
||||||
|
Name: vol.Name,
|
||||||
|
Driver: vol.Driver,
|
||||||
|
DriverOpts: vol.DriverOpts,
|
||||||
|
// Labels: defaultLabels,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, network := range conf.Networks {
|
||||||
|
_, err := e.client.NetworkCreate(noContext, network.Name, types.NetworkCreate{
|
||||||
|
Driver: network.Driver,
|
||||||
|
Options: network.DriverOpts,
|
||||||
|
// Labels: defaultLabels,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *engine) Exec(proc *backend.Step) error {
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
config := toConfig(proc)
|
||||||
|
hostConfig := toHostConfig(proc)
|
||||||
|
|
||||||
|
// create pull options with encoded authorization credentials.
|
||||||
|
pullopts := types.ImagePullOptions{}
|
||||||
|
if proc.AuthConfig.Username != "" && proc.AuthConfig.Password != "" {
|
||||||
|
pullopts.RegistryAuth, _ = encodeAuthToBase64(proc.AuthConfig)
|
||||||
|
}
|
||||||
|
|
||||||
|
// automatically pull the latest version of the image if requested
|
||||||
|
// by the process configuration.
|
||||||
|
if proc.Pull {
|
||||||
|
rc, perr := e.client.ImagePull(ctx, config.Image, pullopts)
|
||||||
|
if perr == nil {
|
||||||
|
io.Copy(ioutil.Discard, rc)
|
||||||
|
rc.Close()
|
||||||
|
}
|
||||||
|
// fix for drone/drone#1917
|
||||||
|
if perr != nil && proc.AuthConfig.Password != "" {
|
||||||
|
return perr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := e.client.ContainerCreate(ctx, config, hostConfig, nil, proc.Name)
|
||||||
|
if client.IsErrImageNotFound(err) {
|
||||||
|
// automatically pull and try to re-create the image if the
|
||||||
|
// failure is caused because the image does not exist.
|
||||||
|
rc, perr := e.client.ImagePull(ctx, config.Image, pullopts)
|
||||||
|
if perr != nil {
|
||||||
|
return perr
|
||||||
|
}
|
||||||
|
io.Copy(ioutil.Discard, rc)
|
||||||
|
rc.Close()
|
||||||
|
|
||||||
|
_, err = e.client.ContainerCreate(ctx, config, hostConfig, nil, proc.Name)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, net := range proc.Networks {
|
||||||
|
err = e.client.NetworkConnect(ctx, net.Name, proc.Name, &network.EndpointSettings{
|
||||||
|
Aliases: net.Aliases,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if proc.Network != "host" { // or bridge, overlay, none, internal, container:<name> ....
|
||||||
|
// err = e.client.NetworkConnect(ctx, proc.Network, proc.Name, &network.EndpointSettings{
|
||||||
|
// Aliases: proc.NetworkAliases,
|
||||||
|
// })
|
||||||
|
// if err != nil {
|
||||||
|
// return err
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
return e.client.ContainerStart(ctx, proc.Name, startOpts)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *engine) Kill(proc *backend.Step) error {
|
||||||
|
return e.client.ContainerKill(noContext, proc.Name, "9")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *engine) Wait(proc *backend.Step) (*backend.State, error) {
|
||||||
|
_, err := e.client.ContainerWait(noContext, proc.Name)
|
||||||
|
if err != nil {
|
||||||
|
// todo
|
||||||
|
}
|
||||||
|
|
||||||
|
info, err := e.client.ContainerInspect(noContext, proc.Name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if info.State.Running {
|
||||||
|
// todo
|
||||||
|
}
|
||||||
|
|
||||||
|
return &backend.State{
|
||||||
|
Exited: true,
|
||||||
|
ExitCode: info.State.ExitCode,
|
||||||
|
OOMKilled: info.State.OOMKilled,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *engine) Tail(proc *backend.Step) (io.ReadCloser, error) {
|
||||||
|
logs, err := e.client.ContainerLogs(noContext, proc.Name, logsOpts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
rc, wc := io.Pipe()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
stdcopy.StdCopy(wc, wc, logs)
|
||||||
|
logs.Close()
|
||||||
|
wc.Close()
|
||||||
|
rc.Close()
|
||||||
|
}()
|
||||||
|
return rc, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *engine) Destroy(conf *backend.Config) error {
|
||||||
|
for _, stage := range conf.Stages {
|
||||||
|
for _, step := range stage.Steps {
|
||||||
|
e.client.ContainerKill(noContext, step.Name, "9")
|
||||||
|
e.client.ContainerRemove(noContext, step.Name, removeOpts)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, volume := range conf.Volumes {
|
||||||
|
e.client.VolumeRemove(noContext, volume.Name, true)
|
||||||
|
}
|
||||||
|
for _, network := range conf.Networks {
|
||||||
|
e.client.NetworkRemove(noContext, network.Name)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
noContext = context.Background()
|
||||||
|
|
||||||
|
startOpts = types.ContainerStartOptions{}
|
||||||
|
|
||||||
|
removeOpts = types.ContainerRemoveOptions{
|
||||||
|
RemoveVolumes: true,
|
||||||
|
RemoveLinks: false,
|
||||||
|
Force: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
logsOpts = types.ContainerLogsOptions{
|
||||||
|
Follow: true,
|
||||||
|
ShowStdout: true,
|
||||||
|
ShowStderr: true,
|
||||||
|
Details: false,
|
||||||
|
Timestamps: false,
|
||||||
|
}
|
||||||
|
)
|
44
vendor/github.com/cncd/pipeline/pipeline/backend/docker/pool.go
generated
vendored
Normal file
44
vendor/github.com/cncd/pipeline/pipeline/backend/docker/pool.go
generated
vendored
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
package docker
|
||||||
|
|
||||||
|
// import (
|
||||||
|
// "context"
|
||||||
|
//
|
||||||
|
// "github.com/cncd/pipeline/pipeline/backend"
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
// // Pool manages a pool of Docker clients.
|
||||||
|
// type Pool struct {
|
||||||
|
// queue chan (backend.Engine)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // NewPool returns a Pool.
|
||||||
|
// func NewPool(engines ...backend.Engine) *Pool {
|
||||||
|
// return &Pool{
|
||||||
|
// queue: make(chan backend.Engine, len(engines)),
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // Reserve requests the next available Docker client in the pool.
|
||||||
|
// func (p *Pool) Reserve(c context.Context) backend.Engine {
|
||||||
|
// select {
|
||||||
|
// case <-c.Done():
|
||||||
|
// case engine := <-p.queue:
|
||||||
|
// return engine
|
||||||
|
// }
|
||||||
|
// return nil
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // Release releases the Docker client back to the pool.
|
||||||
|
// func (p *Pool) Release(engine backend.Engine) {
|
||||||
|
// p.queue <- engine
|
||||||
|
// }
|
||||||
|
|
||||||
|
// pool := docker.Pool(
|
||||||
|
// docker.FromEnvironmentMust(),
|
||||||
|
// docker.FromEnvironmentMust(),
|
||||||
|
// docker.FromEnvironmentMust(),
|
||||||
|
// docker.FromEnvironmentMust(),
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
// client := pool.Reserve()
|
||||||
|
// defer pool.Release(client)
|
112
vendor/github.com/cncd/pipeline/pipeline/backend/types.go
generated
vendored
Normal file
112
vendor/github.com/cncd/pipeline/pipeline/backend/types.go
generated
vendored
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
package backend
|
||||||
|
|
||||||
|
type (
|
||||||
|
// Config defines the runtime configuration of a pipeline.
|
||||||
|
Config struct {
|
||||||
|
Stages []*Stage `json:"pipeline"` // pipeline stages
|
||||||
|
Networks []*Network `json:"networks"` // network definitions
|
||||||
|
Volumes []*Volume `json:"volumes"` // volume definitions
|
||||||
|
Secrets []*Secret `json:"secrets"` // secret definitions
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stage denotes a collection of one or more steps.
|
||||||
|
Stage struct {
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
Alias string `json:"alias,omitempty"`
|
||||||
|
Steps []*Step `json:"steps,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step defines a container process.
|
||||||
|
Step struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Alias string `json:"alias,omitempty"`
|
||||||
|
Image string `json:"image,omitempty"`
|
||||||
|
Pull bool `json:"pull,omitempty"`
|
||||||
|
Detached bool `json:"detach,omitempty"`
|
||||||
|
Privileged bool `json:"privileged,omitempty"`
|
||||||
|
WorkingDir string `json:"working_dir,omitempty"`
|
||||||
|
Environment map[string]string `json:"environment,omitempty"`
|
||||||
|
Labels map[string]string `json:"labels,omitempty"`
|
||||||
|
Entrypoint []string `json:"entrypoint,omitempty"`
|
||||||
|
Command []string `json:"command,omitempty"`
|
||||||
|
ExtraHosts []string `json:"extra_hosts,omitempty"`
|
||||||
|
Volumes []string `json:"volumes,omitempty"`
|
||||||
|
Devices []string `json:"devices,omitempty"`
|
||||||
|
Networks []Conn `json:"networks,omitempty"`
|
||||||
|
DNS []string `json:"dns,omitempty"`
|
||||||
|
DNSSearch []string `json:"dns_search,omitempty"`
|
||||||
|
MemSwapLimit int64 `json:"memswap_limit,omitempty"`
|
||||||
|
MemLimit int64 `json:"mem_limit,omitempty"`
|
||||||
|
ShmSize int64 `json:"shm_size,omitempty"`
|
||||||
|
CPUQuota int64 `json:"cpu_quota,omitempty"`
|
||||||
|
CPUShares int64 `json:"cpu_shares,omitempty"`
|
||||||
|
CPUSet string `json:"cpu_set,omitempty"`
|
||||||
|
OnFailure bool `json:"on_failure,omitempty"`
|
||||||
|
OnSuccess bool `json:"on_success,omitempty"`
|
||||||
|
AuthConfig Auth `json:"auth_config,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Auth defines registry authentication credentials.
|
||||||
|
Auth struct {
|
||||||
|
Username string `json:"username,omitempty"`
|
||||||
|
Password string `json:"password,omitempty"`
|
||||||
|
Email string `json:"email,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Conn defines a container network connection.
|
||||||
|
Conn struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Aliases []string `json:"aliases"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Network defines a container network.
|
||||||
|
Network struct {
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
Driver string `json:"driver,omitempty"`
|
||||||
|
DriverOpts map[string]string `json:"driver_opts,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Volume defines a container volume.
|
||||||
|
Volume struct {
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
Driver string `json:"driver,omitempty"`
|
||||||
|
DriverOpts map[string]string `json:"driver_opts,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Secret defines a runtime secret
|
||||||
|
Secret struct {
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
Value string `json:"value,omitempty"`
|
||||||
|
Mount string `json:"mount,omitempty"`
|
||||||
|
Mask bool `json:"mask,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// State defines a container state.
|
||||||
|
State struct {
|
||||||
|
// Container exit code
|
||||||
|
ExitCode int `json:"exit_code"`
|
||||||
|
// Container exited, true or false
|
||||||
|
Exited bool `json:"exited"`
|
||||||
|
// Container is oom killed, true or false
|
||||||
|
OOMKilled bool `json:"oom_killed"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// // State defines the pipeline and process state.
|
||||||
|
// State struct {
|
||||||
|
// Pipeline struct {
|
||||||
|
// // Current pipeline step
|
||||||
|
// Step *Step `json:"step"`
|
||||||
|
// // Current pipeline error state
|
||||||
|
// Error error `json:"error"`
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Process struct {
|
||||||
|
// // Container exit code
|
||||||
|
// ExitCode int `json:"exit_code"`
|
||||||
|
// // Container exited, true or false
|
||||||
|
// Exited bool `json:"exited"`
|
||||||
|
// // Container is oom killed, true or false
|
||||||
|
// OOMKilled bool `json:"oom_killed"`
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
)
|
38
vendor/github.com/cncd/pipeline/pipeline/error.go
generated
vendored
Normal file
38
vendor/github.com/cncd/pipeline/pipeline/error.go
generated
vendored
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
package pipeline
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ErrSkip is used as a return value when container execution should be
|
||||||
|
// skipped at runtime. It is not returned as an error by any function.
|
||||||
|
ErrSkip = errors.New("Skipped")
|
||||||
|
|
||||||
|
// ErrCancel is used as a return value when the container execution receives
|
||||||
|
// a cancellation signal from the context.
|
||||||
|
ErrCancel = errors.New("Cancelled")
|
||||||
|
)
|
||||||
|
|
||||||
|
// An ExitError reports an unsuccessful exit.
|
||||||
|
type ExitError struct {
|
||||||
|
Name string
|
||||||
|
Code int
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error returns the error message in string format.
|
||||||
|
func (e *ExitError) Error() string {
|
||||||
|
return fmt.Sprintf("%s : exit code %d", e.Name, e.Code)
|
||||||
|
}
|
||||||
|
|
||||||
|
// An OomError reports the process received an OOMKill from the kernel.
|
||||||
|
type OomError struct {
|
||||||
|
Name string
|
||||||
|
Code int
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error reteurns the error message in string format.
|
||||||
|
func (e *OomError) Error() string {
|
||||||
|
return fmt.Sprintf("%s : received oom kill", e.Name)
|
||||||
|
}
|
208
vendor/github.com/cncd/pipeline/pipeline/frontend/metadata.go
generated
vendored
Normal file
208
vendor/github.com/cncd/pipeline/pipeline/frontend/metadata.go
generated
vendored
Normal file
@ -0,0 +1,208 @@
|
|||||||
|
package frontend
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Event types corresponding to scm hooks.
|
||||||
|
const (
|
||||||
|
EventPush = "push"
|
||||||
|
EventPull = "pull_request"
|
||||||
|
EventTag = "tag"
|
||||||
|
EventDeploy = "deployment"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
// Metadata defines runtime m.
|
||||||
|
Metadata struct {
|
||||||
|
ID string `json:"id,omitempty"`
|
||||||
|
Repo Repo `json:"repo,omitempty"`
|
||||||
|
Curr Build `json:"curr,omitempty"`
|
||||||
|
Prev Build `json:"prev,omitempty"`
|
||||||
|
Job Job `json:"job,omitempty"`
|
||||||
|
Sys System `json:"sys,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Repo defines runtime metadata for a repository.
|
||||||
|
Repo struct {
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
Link string `json:"link,omitempty"`
|
||||||
|
Remote string `json:"remote,omitempty"`
|
||||||
|
Private bool `json:"private,omitempty"`
|
||||||
|
Secrets []Secret `json:"secrets,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build defines runtime metadata for a build.
|
||||||
|
Build struct {
|
||||||
|
Number int `json:"number,omitempty"`
|
||||||
|
Created int64 `json:"created,omitempty"`
|
||||||
|
Started int64 `json:"started,omitempty"`
|
||||||
|
Finished int64 `json:"finished,omitempty"`
|
||||||
|
Timeout int64 `json:"timeout,omitempty"`
|
||||||
|
Status string `json:"status,omitempty"`
|
||||||
|
Event string `json:"event,omitempty"`
|
||||||
|
Link string `json:"link,omitempty"`
|
||||||
|
Target string `json:"target,omitempty"`
|
||||||
|
Trusted bool `json:"trusted,omitempty"`
|
||||||
|
Commit Commit `json:"commit,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Commit defines runtime metadata for a commit.
|
||||||
|
Commit struct {
|
||||||
|
Sha string `json:"sha,omitempty"`
|
||||||
|
Ref string `json:"ref,omitempty"`
|
||||||
|
Refspec string `json:"refspec,omitempty"`
|
||||||
|
Branch string `json:"branch,omitempty"`
|
||||||
|
Message string `json:"message,omitempty"`
|
||||||
|
Author Author `json:"author,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Author defines runtime metadata for a commit author.
|
||||||
|
Author struct {
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
Email string `json:"email,omitempty"`
|
||||||
|
Avatar string `json:"avatar,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Job defines runtime metadata for a job.
|
||||||
|
Job struct {
|
||||||
|
Number int `json:"number,omitempty"`
|
||||||
|
Matrix map[string]string `json:"matrix,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Secret defines a runtime secret
|
||||||
|
Secret struct {
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
Value string `json:"value,omitempty"`
|
||||||
|
Mount string `json:"mount,omitempty"`
|
||||||
|
Mask bool `json:"mask,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// System defines runtime metadata for a ci/cd system.
|
||||||
|
System struct {
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
Host string `json:"host,omitempty"`
|
||||||
|
Link string `json:"link,omitempty"`
|
||||||
|
Arch string `json:"arch,omitempty"`
|
||||||
|
Version string `json:"version,omitempty"`
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// Environ returns the metadata as a map of environment variables.
|
||||||
|
func (m *Metadata) Environ() map[string]string {
|
||||||
|
params := map[string]string{
|
||||||
|
"CI_REPO": m.Repo.Name,
|
||||||
|
"CI_REPO_NAME": m.Repo.Name,
|
||||||
|
"CI_REPO_LINK": m.Repo.Link,
|
||||||
|
"CI_REPO_REMOTE": m.Repo.Remote,
|
||||||
|
"CI_REMOTE_URL": m.Repo.Remote,
|
||||||
|
"CI_REPO_PRIVATE": strconv.FormatBool(m.Repo.Private),
|
||||||
|
"CI_BUILD_NUMBER": strconv.Itoa(m.Curr.Number),
|
||||||
|
"CI_BUILD_CREATED": strconv.FormatInt(m.Curr.Created, 10),
|
||||||
|
"CI_BUILD_STARTED": strconv.FormatInt(m.Curr.Started, 10),
|
||||||
|
"CI_BUILD_FINISHED": strconv.FormatInt(m.Curr.Finished, 10),
|
||||||
|
"CI_BUILD_STATUS": m.Curr.Status,
|
||||||
|
"CI_BUILD_EVENT": m.Curr.Event,
|
||||||
|
"CI_BUILD_LINK": m.Curr.Link,
|
||||||
|
"CI_BUILD_TARGET": m.Curr.Target,
|
||||||
|
"CI_COMMIT_SHA": m.Curr.Commit.Sha,
|
||||||
|
"CI_COMMIT_REF": m.Curr.Commit.Ref,
|
||||||
|
"CI_COMMIT_REFSPEC": m.Curr.Commit.Refspec,
|
||||||
|
"CI_COMMIT_BRANCH": m.Curr.Commit.Branch,
|
||||||
|
"CI_COMMIT_MESSAGE": m.Curr.Commit.Message,
|
||||||
|
"CI_COMMIT_AUTHOR": m.Curr.Commit.Author.Name,
|
||||||
|
"CI_COMMIT_AUTHOR_NAME": m.Curr.Commit.Author.Name,
|
||||||
|
"CI_COMMIT_AUTHOR_EMAIL": m.Curr.Commit.Author.Email,
|
||||||
|
"CI_COMMIT_AUTHOR_AVATAR": m.Curr.Commit.Author.Avatar,
|
||||||
|
"CI_PREV_BUILD_NUMBER": strconv.Itoa(m.Prev.Number),
|
||||||
|
"CI_PREV_BUILD_CREATED": strconv.FormatInt(m.Prev.Created, 10),
|
||||||
|
"CI_PREV_BUILD_STARTED": strconv.FormatInt(m.Prev.Started, 10),
|
||||||
|
"CI_PREV_BUILD_FINISHED": strconv.FormatInt(m.Prev.Finished, 10),
|
||||||
|
"CI_PREV_BUILD_STATUS": m.Prev.Status,
|
||||||
|
"CI_PREV_BUILD_EVENT": m.Prev.Event,
|
||||||
|
"CI_PREV_BUILD_LINK": m.Prev.Link,
|
||||||
|
"CI_PREV_COMMIT_SHA": m.Prev.Commit.Sha,
|
||||||
|
"CI_PREV_COMMIT_REF": m.Prev.Commit.Ref,
|
||||||
|
"CI_PREV_COMMIT_REFSPEC": m.Prev.Commit.Refspec,
|
||||||
|
"CI_PREV_COMMIT_BRANCH": m.Prev.Commit.Branch,
|
||||||
|
"CI_PREV_COMMIT_MESSAGE": m.Prev.Commit.Message,
|
||||||
|
"CI_PREV_COMMIT_AUTHOR": m.Prev.Commit.Author.Name,
|
||||||
|
"CI_PREV_COMMIT_AUTHOR_NAME": m.Prev.Commit.Author.Name,
|
||||||
|
"CI_PREV_COMMIT_AUTHOR_EMAIL": m.Prev.Commit.Author.Email,
|
||||||
|
"CI_PREV_COMMIT_AUTHOR_AVATAR": m.Prev.Commit.Author.Avatar,
|
||||||
|
"CI_JOB_NUMBER": strconv.Itoa(m.Job.Number),
|
||||||
|
"CI_SYSTEM": m.Sys.Name,
|
||||||
|
"CI_SYSTEM_NAME": m.Sys.Name,
|
||||||
|
"CI_SYSTEM_LINK": m.Sys.Link,
|
||||||
|
"CI_SYSTEM_HOST": m.Sys.Host,
|
||||||
|
"CI_SYSTEM_ARCH": m.Sys.Arch,
|
||||||
|
"CI_SYSTEM_VERSION": m.Sys.Version,
|
||||||
|
"CI": m.Sys.Name,
|
||||||
|
}
|
||||||
|
if m.Curr.Event == EventTag {
|
||||||
|
params["CI_TAG"] = strings.TrimPrefix(m.Curr.Commit.Ref, "refs/tags/")
|
||||||
|
}
|
||||||
|
if m.Curr.Event == EventPull {
|
||||||
|
params["CI_PULL_REQUEST"] = pullRegexp.FindString(m.Curr.Commit.Ref)
|
||||||
|
}
|
||||||
|
return params
|
||||||
|
}
|
||||||
|
|
||||||
|
// EnvironDrone returns metadata as a map of DRONE_ environment variables.
|
||||||
|
// This is here for backward compatibility and will eventually be removed.
|
||||||
|
func (m *Metadata) EnvironDrone() map[string]string {
|
||||||
|
// MISSING PARAMETERS
|
||||||
|
// * DRONE_REPO_TRUSTED
|
||||||
|
// * DRONE_YAML_VERIFIED
|
||||||
|
// * DRONE_YAML_VERIFIED
|
||||||
|
params := map[string]string{
|
||||||
|
"CI": "drone",
|
||||||
|
"DRONE": "true",
|
||||||
|
"DRONE_ARCH": "linux/amd64",
|
||||||
|
"DRONE_REPO": m.Repo.Name,
|
||||||
|
"DRONE_REPO_SCM": "git",
|
||||||
|
"DRONE_REPO_OWNER": strings.Split(m.Repo.Name, "/")[0],
|
||||||
|
"DRONE_REPO_NAME": strings.Split(m.Repo.Name, "/")[0],
|
||||||
|
"DRONE_REPO_LINK": m.Repo.Link,
|
||||||
|
"DRONE_REPO_BRANCH": m.Curr.Commit.Branch,
|
||||||
|
"DRONE_REPO_PRIVATE": fmt.Sprintf("%v", m.Repo.Private),
|
||||||
|
"DRONE_REPO_TRUSTED": "false", // TODO should this be added?
|
||||||
|
"DRONE_REMOTE_URL": m.Repo.Remote,
|
||||||
|
"DRONE_COMMIT_SHA": m.Curr.Commit.Sha,
|
||||||
|
"DRONE_COMMIT_REF": m.Curr.Commit.Ref,
|
||||||
|
"DRONE_COMMIT_REFSPEC": m.Curr.Commit.Refspec,
|
||||||
|
"DRONE_COMMIT_BRANCH": m.Curr.Commit.Branch,
|
||||||
|
"DRONE_COMMIT_LINK": m.Curr.Link,
|
||||||
|
"DRONE_COMMIT_MESSAGE": m.Curr.Commit.Message,
|
||||||
|
"DRONE_COMMIT_AUTHOR": m.Curr.Commit.Author.Name,
|
||||||
|
"DRONE_COMMIT_AUTHOR_EMAIL": m.Curr.Commit.Author.Email,
|
||||||
|
"DRONE_COMMIT_AUTHOR_AVATAR": m.Curr.Commit.Author.Avatar,
|
||||||
|
"DRONE_BUILD_NUMBER": fmt.Sprintf("%d", m.Curr.Number),
|
||||||
|
"DRONE_BUILD_EVENT": m.Curr.Event,
|
||||||
|
"DRONE_BUILD_LINK": fmt.Sprintf("%s/%s/%d", m.Sys.Link, m.Repo.Name, m.Curr.Number),
|
||||||
|
"DRONE_BUILD_CREATED": fmt.Sprintf("%d", m.Curr.Created),
|
||||||
|
"DRONE_BUILD_STARTED": fmt.Sprintf("%d", m.Curr.Started),
|
||||||
|
"DRONE_BUILD_FINISHED": fmt.Sprintf("%d", m.Curr.Finished),
|
||||||
|
"DRONE_JOB_NUMBER": fmt.Sprintf("%d", m.Job.Number),
|
||||||
|
"DRONE_JOB_STARTED": fmt.Sprintf("%d", m.Curr.Started), // ISSUE: no job started
|
||||||
|
"DRONE_BRANCH": m.Curr.Commit.Branch,
|
||||||
|
"DRONE_COMMIT": m.Curr.Commit.Sha,
|
||||||
|
"DRONE_VERSION": m.Sys.Version,
|
||||||
|
"DRONE_DEPLOY_TO": m.Curr.Target,
|
||||||
|
"DRONE_PREV_BUILD_STATUS": m.Prev.Status,
|
||||||
|
"DRONE_PREV_BUILD_NUMBER": fmt.Sprintf("%v", m.Prev.Number),
|
||||||
|
"DRONE_PREV_COMMIT_SHA": m.Prev.Commit.Sha,
|
||||||
|
}
|
||||||
|
if m.Curr.Event == EventTag {
|
||||||
|
params["DRONE_TAG"] = strings.TrimPrefix(m.Curr.Commit.Ref, "refs/tags/")
|
||||||
|
}
|
||||||
|
if m.Curr.Event == EventPull {
|
||||||
|
params["DRONE_PULL_REQUEST"] = pullRegexp.FindString(m.Curr.Commit.Ref)
|
||||||
|
}
|
||||||
|
return params
|
||||||
|
}
|
||||||
|
|
||||||
|
var pullRegexp = regexp.MustCompile("\\d+")
|
168
vendor/github.com/cncd/pipeline/pipeline/frontend/yaml/compiler/compiler.go
generated
vendored
Normal file
168
vendor/github.com/cncd/pipeline/pipeline/frontend/yaml/compiler/compiler.go
generated
vendored
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
package compiler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/cncd/pipeline/pipeline/backend"
|
||||||
|
"github.com/cncd/pipeline/pipeline/frontend"
|
||||||
|
"github.com/cncd/pipeline/pipeline/frontend/yaml"
|
||||||
|
// libcompose "github.com/docker/libcompose/yaml"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TODO(bradrydzewski) compiler should handle user-defined volumes from YAML
|
||||||
|
// TODO(bradrydzewski) compiler should handle user-defined networks from YAML
|
||||||
|
|
||||||
|
// Compiler compiles the yaml
|
||||||
|
type Compiler struct {
|
||||||
|
local bool
|
||||||
|
escalated []string
|
||||||
|
prefix string
|
||||||
|
volumes []string
|
||||||
|
env map[string]string
|
||||||
|
base string
|
||||||
|
path string
|
||||||
|
metadata frontend.Metadata
|
||||||
|
aliases []string
|
||||||
|
}
|
||||||
|
|
||||||
|
// New creates a new Compiler with options.
|
||||||
|
func New(opts ...Option) *Compiler {
|
||||||
|
compiler := new(Compiler)
|
||||||
|
compiler.env = map[string]string{}
|
||||||
|
for _, opt := range opts {
|
||||||
|
opt(compiler)
|
||||||
|
}
|
||||||
|
return compiler
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compile compiles the YAML configuration to the pipeline intermediate
|
||||||
|
// representation configuration format.
|
||||||
|
func (c *Compiler) Compile(conf *yaml.Config) *backend.Config {
|
||||||
|
config := new(backend.Config)
|
||||||
|
|
||||||
|
// create a default volume
|
||||||
|
config.Volumes = append(config.Volumes, &backend.Volume{
|
||||||
|
Name: fmt.Sprintf("%s_default", c.prefix),
|
||||||
|
Driver: "local",
|
||||||
|
})
|
||||||
|
|
||||||
|
// create a default network
|
||||||
|
config.Networks = append(config.Networks, &backend.Network{
|
||||||
|
Name: fmt.Sprintf("%s_default", c.prefix),
|
||||||
|
Driver: "bridge",
|
||||||
|
})
|
||||||
|
|
||||||
|
// overrides the default workspace paths when specified
|
||||||
|
// in the YAML file.
|
||||||
|
if len(conf.Workspace.Base) != 0 {
|
||||||
|
c.base = conf.Workspace.Base
|
||||||
|
}
|
||||||
|
if len(conf.Workspace.Path) != 0 {
|
||||||
|
c.path = conf.Workspace.Path
|
||||||
|
}
|
||||||
|
|
||||||
|
// add default clone step
|
||||||
|
if c.local == false && len(conf.Clone.Containers) == 0 {
|
||||||
|
container := &yaml.Container{
|
||||||
|
Name: "clone",
|
||||||
|
Image: "plugins/git:latest",
|
||||||
|
Vargs: map[string]interface{}{"depth": "0"},
|
||||||
|
}
|
||||||
|
name := fmt.Sprintf("%s_clone", c.prefix)
|
||||||
|
step := c.createProcess(name, container)
|
||||||
|
|
||||||
|
stage := new(backend.Stage)
|
||||||
|
stage.Name = name
|
||||||
|
stage.Alias = "clone"
|
||||||
|
stage.Steps = append(stage.Steps, step)
|
||||||
|
|
||||||
|
config.Stages = append(config.Stages, stage)
|
||||||
|
} else if c.local == false {
|
||||||
|
for i, container := range conf.Clone.Containers {
|
||||||
|
if !container.Constraints.Match(c.metadata) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
stage := new(backend.Stage)
|
||||||
|
stage.Name = fmt.Sprintf("%s_clone_%v", c.prefix, i)
|
||||||
|
stage.Alias = container.Name
|
||||||
|
|
||||||
|
name := fmt.Sprintf("%s_clone_%d", c.prefix, i)
|
||||||
|
step := c.createProcess(name, container)
|
||||||
|
stage.Steps = append(stage.Steps, step)
|
||||||
|
|
||||||
|
config.Stages = append(config.Stages, stage)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// add services steps
|
||||||
|
if len(conf.Services.Containers) != 0 {
|
||||||
|
stage := new(backend.Stage)
|
||||||
|
stage.Name = fmt.Sprintf("%s_services", c.prefix)
|
||||||
|
stage.Alias = "services"
|
||||||
|
|
||||||
|
for _, container := range conf.Services.Containers {
|
||||||
|
c.aliases = append(c.aliases, container.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, container := range conf.Services.Containers {
|
||||||
|
name := fmt.Sprintf("%s_services_%d", c.prefix, i)
|
||||||
|
step := c.createProcess(name, container)
|
||||||
|
stage.Steps = append(stage.Steps, step)
|
||||||
|
|
||||||
|
}
|
||||||
|
config.Stages = append(config.Stages, stage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// add pipeline steps. 1 pipeline step per stage, at the moment
|
||||||
|
var stage *backend.Stage
|
||||||
|
var group string
|
||||||
|
for i, container := range conf.Pipeline.Containers {
|
||||||
|
if !container.Constraints.Match(c.metadata) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if stage == nil || group != container.Group || container.Group == "" {
|
||||||
|
group = container.Group
|
||||||
|
|
||||||
|
stage = new(backend.Stage)
|
||||||
|
stage.Name = fmt.Sprintf("%s_stage_%v", c.prefix, i)
|
||||||
|
stage.Alias = container.Name
|
||||||
|
config.Stages = append(config.Stages, stage)
|
||||||
|
}
|
||||||
|
|
||||||
|
name := fmt.Sprintf("%s_step_%d", c.prefix, i)
|
||||||
|
step := c.createProcess(name, container)
|
||||||
|
stage.Steps = append(stage.Steps, step)
|
||||||
|
}
|
||||||
|
|
||||||
|
return config
|
||||||
|
}
|
||||||
|
|
||||||
|
// func setupNetwork(step *backend.Step, network *libcompose.Network) {
|
||||||
|
// step.Networks = append(step.Networks, backend.Conn{
|
||||||
|
// Name: network.Name,
|
||||||
|
// // Aliases:
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// func setupVolume(step *backend.Step, volume *libcompose.Volume) {
|
||||||
|
// step.Volumes = append(step.Volumes, volume.String())
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// var (
|
||||||
|
// // Default plugin used to clone the repository.
|
||||||
|
// defaultCloneImage = "plugins/git:latest"
|
||||||
|
//
|
||||||
|
// // Default plugin settings used to clone the repository.
|
||||||
|
// defaultCloneVargs = map[string]interface{}{
|
||||||
|
// "depth": 0,
|
||||||
|
// }
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
// // defaultClone returns the default step for cloning an image.
|
||||||
|
// func defaultClone() *yaml.Container {
|
||||||
|
// return &yaml.Container{
|
||||||
|
// Image: defaultCloneImage,
|
||||||
|
// Vargs: defaultCloneVargs,
|
||||||
|
// }
|
||||||
|
// }
|
145
vendor/github.com/cncd/pipeline/pipeline/frontend/yaml/compiler/convert.go
generated
vendored
Normal file
145
vendor/github.com/cncd/pipeline/pipeline/frontend/yaml/compiler/convert.go
generated
vendored
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
package compiler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"path"
|
||||||
|
|
||||||
|
"github.com/cncd/pipeline/pipeline/backend"
|
||||||
|
"github.com/cncd/pipeline/pipeline/frontend/yaml"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *Compiler) createProcess(name string, container *yaml.Container) *backend.Step {
|
||||||
|
var (
|
||||||
|
detached bool
|
||||||
|
workingdir string
|
||||||
|
|
||||||
|
workspace = fmt.Sprintf("%s_default:%s", c.prefix, c.base)
|
||||||
|
privileged = container.Privileged
|
||||||
|
entrypoint = container.Entrypoint
|
||||||
|
command = container.Command
|
||||||
|
image = expandImage(container.Image)
|
||||||
|
// network = container.Network
|
||||||
|
)
|
||||||
|
|
||||||
|
networks := []backend.Conn{
|
||||||
|
backend.Conn{
|
||||||
|
Name: fmt.Sprintf("%s_default", c.prefix),
|
||||||
|
Aliases: c.aliases,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
volumes := []string{
|
||||||
|
workspace,
|
||||||
|
}
|
||||||
|
for _, volume := range container.Volumes.Volumes {
|
||||||
|
volumes = append(volumes, volume.String())
|
||||||
|
}
|
||||||
|
// if network == "" {
|
||||||
|
// network = fmt.Sprintf("%s_default", c.prefix)
|
||||||
|
// for _, alias := range c.aliases {
|
||||||
|
// // if alias != container.Name {
|
||||||
|
// aliases = append(aliases, alias)
|
||||||
|
// // }
|
||||||
|
// }
|
||||||
|
// } // host, bridge, none, container:<name>, overlay
|
||||||
|
|
||||||
|
// append default environment variables
|
||||||
|
environment := map[string]string{}
|
||||||
|
for k, v := range container.Environment {
|
||||||
|
environment[k] = v
|
||||||
|
}
|
||||||
|
for k, v := range c.env {
|
||||||
|
switch v {
|
||||||
|
case "", "0", "false":
|
||||||
|
continue
|
||||||
|
default:
|
||||||
|
environment[k] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
environment["CI_WORKSPACE"] = path.Join(c.base, c.path)
|
||||||
|
environment["DRONE_WORKSPACE"] = path.Join(c.base, c.path)
|
||||||
|
|
||||||
|
if !isService(container) {
|
||||||
|
workingdir = path.Join(c.base, c.path)
|
||||||
|
}
|
||||||
|
|
||||||
|
if isService(container) {
|
||||||
|
detached = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if isPlugin(container) {
|
||||||
|
paramsToEnv(container.Vargs, environment)
|
||||||
|
|
||||||
|
if imageMatches(container.Image, c.escalated) {
|
||||||
|
privileged = true
|
||||||
|
entrypoint = []string{}
|
||||||
|
command = []string{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if isShell(container) {
|
||||||
|
entrypoint = []string{"/bin/sh", "-c"}
|
||||||
|
command = []string{"echo $CI_SCRIPT | base64 -d | /bin/sh -e"}
|
||||||
|
environment["CI_SCRIPT"] = generateScriptPosix(container.Commands)
|
||||||
|
environment["HOME"] = "/root"
|
||||||
|
environment["SHELL"] = "/bin/sh"
|
||||||
|
}
|
||||||
|
|
||||||
|
return &backend.Step{
|
||||||
|
Name: name,
|
||||||
|
Alias: container.Name,
|
||||||
|
Image: image,
|
||||||
|
Pull: container.Pull,
|
||||||
|
Detached: detached,
|
||||||
|
Privileged: privileged,
|
||||||
|
WorkingDir: workingdir,
|
||||||
|
Environment: environment,
|
||||||
|
Labels: container.Labels,
|
||||||
|
Entrypoint: entrypoint,
|
||||||
|
Command: command,
|
||||||
|
ExtraHosts: container.ExtraHosts,
|
||||||
|
Volumes: volumes,
|
||||||
|
Devices: container.Devices,
|
||||||
|
Networks: networks,
|
||||||
|
DNS: container.DNS,
|
||||||
|
DNSSearch: container.DNSSearch,
|
||||||
|
MemSwapLimit: int64(container.MemSwapLimit),
|
||||||
|
MemLimit: int64(container.MemLimit),
|
||||||
|
ShmSize: int64(container.ShmSize),
|
||||||
|
CPUQuota: int64(container.CPUQuota),
|
||||||
|
CPUShares: int64(container.CPUShares),
|
||||||
|
CPUSet: container.CPUSet,
|
||||||
|
AuthConfig: backend.Auth{
|
||||||
|
Username: container.AuthConfig.Username,
|
||||||
|
Password: container.AuthConfig.Password,
|
||||||
|
Email: container.AuthConfig.Email,
|
||||||
|
},
|
||||||
|
OnSuccess: container.Constraints.Status.Match("success"),
|
||||||
|
OnFailure: (len(container.Constraints.Status.Include)+
|
||||||
|
len(container.Constraints.Status.Exclude) != 0) &&
|
||||||
|
container.Constraints.Status.Match("failure"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func imageMatches(image string, to []string) bool {
|
||||||
|
image = trimImage(image)
|
||||||
|
for _, i := range to {
|
||||||
|
if image == i {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func isPlugin(c *yaml.Container) bool {
|
||||||
|
return len(c.Vargs) != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func isShell(c *yaml.Container) bool {
|
||||||
|
return len(c.Commands) != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func isService(c *yaml.Container) bool {
|
||||||
|
return c.Detached || (isPlugin(c) == false && isShell(c) == false)
|
||||||
|
}
|
36
vendor/github.com/cncd/pipeline/pipeline/frontend/yaml/compiler/image.go
generated
vendored
Normal file
36
vendor/github.com/cncd/pipeline/pipeline/frontend/yaml/compiler/image.go
generated
vendored
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
package compiler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/docker/docker/reference"
|
||||||
|
)
|
||||||
|
|
||||||
|
// trimImage returns the short image name without tag.
|
||||||
|
func trimImage(name string) string {
|
||||||
|
ref, err := reference.ParseNamed(name)
|
||||||
|
if err != nil {
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
return reference.TrimNamed(ref).String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// expandImage returns the fully qualified image name.
|
||||||
|
func expandImage(name string) string {
|
||||||
|
ref, err := reference.ParseNamed(name)
|
||||||
|
if err != nil {
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
return reference.WithDefaultTag(ref).String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// matchImage returns true if the image name matches
|
||||||
|
// an image in the list. Note the image tag is not used
|
||||||
|
// in the matching logic.
|
||||||
|
func matchImage(from string, to ...string) bool {
|
||||||
|
from = trimImage(from)
|
||||||
|
for _, match := range to {
|
||||||
|
if from == match {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
169
vendor/github.com/cncd/pipeline/pipeline/frontend/yaml/compiler/option.go
generated
vendored
Normal file
169
vendor/github.com/cncd/pipeline/pipeline/frontend/yaml/compiler/option.go
generated
vendored
Normal file
@ -0,0 +1,169 @@
|
|||||||
|
package compiler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/cncd/pipeline/pipeline/frontend"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Option configures a compiler option.
|
||||||
|
type Option func(*Compiler)
|
||||||
|
|
||||||
|
// WithVolumes configutes the compiler with default volumes that
|
||||||
|
// are mounted to each container in the pipeline.
|
||||||
|
func WithVolumes(volumes ...string) Option {
|
||||||
|
return func(compiler *Compiler) {
|
||||||
|
compiler.volumes = volumes
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithMetadata configutes the compiler with the repostiory, build
|
||||||
|
// and system metadata. The metadata is used to remove steps from
|
||||||
|
// the compiled pipeline configuration that should be skipped. The
|
||||||
|
// metadata is also added to each container as environment variables.
|
||||||
|
func WithMetadata(metadata frontend.Metadata) Option {
|
||||||
|
return func(compiler *Compiler) {
|
||||||
|
compiler.metadata = metadata
|
||||||
|
|
||||||
|
for k, v := range metadata.Environ() {
|
||||||
|
compiler.env[k] = v
|
||||||
|
}
|
||||||
|
// TODO this is present for backward compatibility and should
|
||||||
|
// be removed in a future version.
|
||||||
|
for k, v := range metadata.EnvironDrone() {
|
||||||
|
compiler.env[k] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithNetrc configures the compiler with netrc authentication
|
||||||
|
// credentials added by default to every container in the pipeline.
|
||||||
|
func WithNetrc(username, password, machine string) Option {
|
||||||
|
return WithEnviron(
|
||||||
|
map[string]string{
|
||||||
|
"CI_NETRC_USERNAME": username,
|
||||||
|
"CI_NETRC_PASSWORD": password,
|
||||||
|
"CI_NETRC_MACHINE": machine,
|
||||||
|
|
||||||
|
// TODO this is present for backward compatibility and should
|
||||||
|
// be removed in a future version.
|
||||||
|
"DRONE_NETRC_USERNAME": username,
|
||||||
|
"DRONE_NETRC_PASSWORD": password,
|
||||||
|
"DRONE_NETRC_MACHINE": machine,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithWorkspace configures the compiler with the workspace base
|
||||||
|
// and path. The workspace base is a volume created at runtime and
|
||||||
|
// mounted into all containers in the pipeline. The base and path
|
||||||
|
// are joined to provide the working directory for all build and
|
||||||
|
// plugin steps in the pipeline.
|
||||||
|
func WithWorkspace(base, path string) Option {
|
||||||
|
return func(compiler *Compiler) {
|
||||||
|
compiler.base = base
|
||||||
|
compiler.path = path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithWorkspaceFromURL configures the compiler with the workspace
|
||||||
|
// base and path based on the repository url.
|
||||||
|
func WithWorkspaceFromURL(base, link string) Option {
|
||||||
|
path := "src"
|
||||||
|
parsed, err := url.Parse(link)
|
||||||
|
if err == nil {
|
||||||
|
path = filepath.Join(path, parsed.Host, parsed.Path)
|
||||||
|
}
|
||||||
|
return WithWorkspace(base, path)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithEscalated configures the compiler to automatically execute
|
||||||
|
// images as privileged containers if the match the given list.
|
||||||
|
func WithEscalated(images ...string) Option {
|
||||||
|
return func(compiler *Compiler) {
|
||||||
|
compiler.escalated = images
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithPrefix configures the compiler with the prefix. The prefix is
|
||||||
|
// used to prefix container, volume and network names to avoid
|
||||||
|
// collision at runtime.
|
||||||
|
func WithPrefix(prefix string) Option {
|
||||||
|
return func(compiler *Compiler) {
|
||||||
|
compiler.prefix = prefix
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithLocal configures the compiler with the local flag. The local
|
||||||
|
// flag indicates the pipeline execution is running in a local development
|
||||||
|
// environment with a mounted local working directory.
|
||||||
|
func WithLocal(local bool) Option {
|
||||||
|
return func(compiler *Compiler) {
|
||||||
|
compiler.local = local
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithEnviron configures the compiler with environment variables
|
||||||
|
// added by default to every container in the pipeline.
|
||||||
|
func WithEnviron(env map[string]string) Option {
|
||||||
|
return func(compiler *Compiler) {
|
||||||
|
for k, v := range env {
|
||||||
|
compiler.env[k] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithProxy configures the compiler with HTTP_PROXY, HTTPS_PROXY,
|
||||||
|
// and NO_PROXY environment variables added by default to every
|
||||||
|
// container in the pipeline.
|
||||||
|
func WithProxy() Option {
|
||||||
|
return WithEnviron(
|
||||||
|
map[string]string{
|
||||||
|
"no_proxy": noProxy,
|
||||||
|
"NO_PROXY": noProxy,
|
||||||
|
"http_proxy": httpProxy,
|
||||||
|
"HTTP_PROXY": httpProxy,
|
||||||
|
"HTTPS_PROXY": httpsProxy,
|
||||||
|
"https_proxy": httpsProxy,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(bradrydzewski) consider an alternate approach to
|
||||||
|
// WithProxy where the proxy strings are passed directly
|
||||||
|
// to the function as named parameters.
|
||||||
|
|
||||||
|
// func WithProxy2(http, https, none string) Option {
|
||||||
|
// return WithEnviron(
|
||||||
|
// map[string]string{
|
||||||
|
// "no_proxy": none,
|
||||||
|
// "NO_PROXY": none,
|
||||||
|
// "http_proxy": http,
|
||||||
|
// "HTTP_PROXY": http,
|
||||||
|
// "HTTPS_PROXY": https,
|
||||||
|
// "https_proxy": https,
|
||||||
|
// },
|
||||||
|
// )
|
||||||
|
// }
|
||||||
|
|
||||||
|
var (
|
||||||
|
noProxy = getenv("no_proxy")
|
||||||
|
httpProxy = getenv("https_proxy")
|
||||||
|
httpsProxy = getenv("https_proxy")
|
||||||
|
)
|
||||||
|
|
||||||
|
// getenv returns the named environment variable.
|
||||||
|
func getenv(name string) (value string) {
|
||||||
|
name = strings.ToUpper(name)
|
||||||
|
if value := os.Getenv(name); value != "" {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
name = strings.ToLower(name)
|
||||||
|
if value := os.Getenv(name); value != "" {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
65
vendor/github.com/cncd/pipeline/pipeline/frontend/yaml/compiler/params.go
generated
vendored
Normal file
65
vendor/github.com/cncd/pipeline/pipeline/frontend/yaml/compiler/params.go
generated
vendored
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
package compiler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
json "github.com/ghodss/yaml"
|
||||||
|
"gopkg.in/yaml.v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// paramsToEnv uses reflection to convert a map[string]interface to a list
|
||||||
|
// of environment variables.
|
||||||
|
func paramsToEnv(from map[string]interface{}, to map[string]string) error {
|
||||||
|
for k, v := range from {
|
||||||
|
if v == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
t := reflect.TypeOf(v)
|
||||||
|
vv := reflect.ValueOf(v)
|
||||||
|
|
||||||
|
k = "PLUGIN_" + strings.ToUpper(k)
|
||||||
|
|
||||||
|
switch t.Kind() {
|
||||||
|
case reflect.Bool:
|
||||||
|
to[k] = strconv.FormatBool(vv.Bool())
|
||||||
|
|
||||||
|
case reflect.String:
|
||||||
|
to[k] = vv.String()
|
||||||
|
|
||||||
|
case reflect.Int, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int8:
|
||||||
|
to[k] = fmt.Sprintf("%v", vv.Int())
|
||||||
|
|
||||||
|
case reflect.Float32, reflect.Float64:
|
||||||
|
to[k] = fmt.Sprintf("%v", vv.Float())
|
||||||
|
|
||||||
|
case reflect.Map:
|
||||||
|
yml, _ := yaml.Marshal(vv.Interface())
|
||||||
|
out, _ := json.YAMLToJSON(yml)
|
||||||
|
to[k] = string(out)
|
||||||
|
|
||||||
|
case reflect.Slice:
|
||||||
|
out, err := yaml.Marshal(vv.Interface())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
in := []string{}
|
||||||
|
err = yaml.Unmarshal(out, &in)
|
||||||
|
if err == nil {
|
||||||
|
to[k] = strings.Join(in, ",")
|
||||||
|
} else {
|
||||||
|
out, err = json.YAMLToJSON(out)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
to[k] = string(out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
52
vendor/github.com/cncd/pipeline/pipeline/frontend/yaml/compiler/script_posix.go
generated
vendored
Normal file
52
vendor/github.com/cncd/pipeline/pipeline/frontend/yaml/compiler/script_posix.go
generated
vendored
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
package compiler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/base64"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// generateScriptPosix is a helper function that generates a build script
|
||||||
|
// for a linux container using the given
|
||||||
|
func generateScriptPosix(commands []string) string {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
for _, command := range commands {
|
||||||
|
escaped := fmt.Sprintf("%q", command)
|
||||||
|
escaped = strings.Replace(escaped, "$", `\$`, -1)
|
||||||
|
buf.WriteString(fmt.Sprintf(
|
||||||
|
traceScript,
|
||||||
|
escaped,
|
||||||
|
command,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
script := fmt.Sprintf(
|
||||||
|
setupScript,
|
||||||
|
buf.String(),
|
||||||
|
)
|
||||||
|
return base64.StdEncoding.EncodeToString([]byte(script))
|
||||||
|
}
|
||||||
|
|
||||||
|
// setupScript is a helper script this is added to the build to ensure
|
||||||
|
// a minimum set of environment variables are set correctly.
|
||||||
|
const setupScript = `
|
||||||
|
if [ -n "$CI_NETRC_MACHINE" ]; then
|
||||||
|
cat <<EOF > $HOME/.netrc
|
||||||
|
machine $CI_NETRC_MACHINE
|
||||||
|
login $CI_NETRC_USERNAME
|
||||||
|
password $CI_NETRC_PASSWORD
|
||||||
|
EOF
|
||||||
|
chmod 0600 $HOME/.netrc
|
||||||
|
fi
|
||||||
|
unset CI_NETRC_USERNAME
|
||||||
|
unset CI_NETRC_PASSWORD
|
||||||
|
unset CI_SCRIPT
|
||||||
|
%s
|
||||||
|
`
|
||||||
|
|
||||||
|
// traceScript is a helper script that is added to the build script
|
||||||
|
// to trace a command.
|
||||||
|
const traceScript = `
|
||||||
|
echo + %s
|
||||||
|
%s
|
||||||
|
`
|
1
vendor/github.com/cncd/pipeline/pipeline/frontend/yaml/compiler/script_win.go
generated
vendored
Normal file
1
vendor/github.com/cncd/pipeline/pipeline/frontend/yaml/compiler/script_win.go
generated
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
package compiler
|
68
vendor/github.com/cncd/pipeline/pipeline/frontend/yaml/config.go
generated
vendored
Normal file
68
vendor/github.com/cncd/pipeline/pipeline/frontend/yaml/config.go
generated
vendored
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
package yaml
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
libcompose "github.com/docker/libcompose/yaml"
|
||||||
|
"gopkg.in/yaml.v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
// Config defines a pipeline configuration.
|
||||||
|
Config struct {
|
||||||
|
Platform string
|
||||||
|
Branches Constraint
|
||||||
|
Workspace Workspace
|
||||||
|
Clone Containers
|
||||||
|
Pipeline Containers
|
||||||
|
Services Containers
|
||||||
|
Networks Networks
|
||||||
|
Volumes Volumes
|
||||||
|
Labels libcompose.SliceorMap
|
||||||
|
}
|
||||||
|
|
||||||
|
// Workspace defines a pipeline workspace.
|
||||||
|
Workspace struct {
|
||||||
|
Base string
|
||||||
|
Path string
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// Parse parses the configuration from bytes b.
|
||||||
|
func Parse(r io.Reader) (*Config, error) {
|
||||||
|
out, err := ioutil.ReadAll(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return ParseBytes(out)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseBytes parses the configuration from bytes b.
|
||||||
|
func ParseBytes(b []byte) (*Config, error) {
|
||||||
|
out := new(Config)
|
||||||
|
err := yaml.Unmarshal(b, out)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseString parses the configuration from string s.
|
||||||
|
func ParseString(s string) (*Config, error) {
|
||||||
|
return ParseBytes(
|
||||||
|
[]byte(s),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseFile parses the configuration from path p.
|
||||||
|
func ParseFile(p string) (*Config, error) {
|
||||||
|
f, err := os.Open(p)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
return Parse(f)
|
||||||
|
}
|
152
vendor/github.com/cncd/pipeline/pipeline/frontend/yaml/constraint.go
generated
vendored
Normal file
152
vendor/github.com/cncd/pipeline/pipeline/frontend/yaml/constraint.go
generated
vendored
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
package yaml
|
||||||
|
|
||||||
|
import (
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/cncd/pipeline/pipeline/frontend"
|
||||||
|
libcompose "github.com/docker/libcompose/yaml"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
// Constraints defines a set of runtime constraints.
|
||||||
|
Constraints struct {
|
||||||
|
Repo Constraint
|
||||||
|
Instance Constraint
|
||||||
|
Platform Constraint
|
||||||
|
Environment Constraint
|
||||||
|
Event Constraint
|
||||||
|
Branch Constraint
|
||||||
|
Status Constraint
|
||||||
|
Matrix ConstraintMap
|
||||||
|
}
|
||||||
|
|
||||||
|
// Constraint defines a runtime constraint.
|
||||||
|
Constraint struct {
|
||||||
|
Include []string
|
||||||
|
Exclude []string
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConstraintMap defines a runtime constraint map.
|
||||||
|
ConstraintMap struct {
|
||||||
|
Include map[string]string
|
||||||
|
Exclude map[string]string
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// Match returns true if all constraints match the given input. If a single
|
||||||
|
// constraint fails a false value is returned.
|
||||||
|
func (c *Constraints) Match(metadata frontend.Metadata) bool {
|
||||||
|
return c.Platform.Match(metadata.Sys.Arch) &&
|
||||||
|
c.Environment.Match(metadata.Curr.Target) &&
|
||||||
|
c.Event.Match(metadata.Curr.Event) &&
|
||||||
|
c.Branch.Match(metadata.Curr.Commit.Branch) &&
|
||||||
|
c.Repo.Match(metadata.Repo.Name) &&
|
||||||
|
c.Matrix.Match(metadata.Job.Matrix)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Match returns true if the string matches the include patterns and does not
|
||||||
|
// match any of the exclude patterns.
|
||||||
|
func (c *Constraint) Match(v string) bool {
|
||||||
|
if c.Excludes(v) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if c.Includes(v) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if len(c.Include) == 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Includes returns true if the string matches the include patterns.
|
||||||
|
func (c *Constraint) Includes(v string) bool {
|
||||||
|
for _, pattern := range c.Include {
|
||||||
|
if ok, _ := filepath.Match(pattern, v); ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Excludes returns true if the string matches the exclude patterns.
|
||||||
|
func (c *Constraint) Excludes(v string) bool {
|
||||||
|
for _, pattern := range c.Exclude {
|
||||||
|
if ok, _ := filepath.Match(pattern, v); ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalYAML unmarshals the constraint.
|
||||||
|
func (c *Constraint) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||||
|
var out1 = struct {
|
||||||
|
Include libcompose.Stringorslice
|
||||||
|
Exclude libcompose.Stringorslice
|
||||||
|
}{}
|
||||||
|
|
||||||
|
var out2 libcompose.Stringorslice
|
||||||
|
|
||||||
|
unmarshal(&out1)
|
||||||
|
unmarshal(&out2)
|
||||||
|
|
||||||
|
c.Exclude = out1.Exclude
|
||||||
|
c.Include = append(
|
||||||
|
out1.Include,
|
||||||
|
out2...,
|
||||||
|
)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Match returns true if the params matches the include key values and does not
|
||||||
|
// match any of the exclude key values.
|
||||||
|
func (c *ConstraintMap) Match(params map[string]string) bool {
|
||||||
|
// when no includes or excludes automatically match
|
||||||
|
if len(c.Include) == 0 && len(c.Exclude) == 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
// exclusions are processed first. So we can include everything and then
|
||||||
|
// selectively include others.
|
||||||
|
if len(c.Exclude) != 0 {
|
||||||
|
var matches int
|
||||||
|
|
||||||
|
for key, val := range c.Exclude {
|
||||||
|
if params[key] == val {
|
||||||
|
matches++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if matches == len(c.Exclude) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for key, val := range c.Include {
|
||||||
|
if params[key] != val {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalYAML unmarshals the constraint map.
|
||||||
|
func (c *ConstraintMap) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||||
|
out1 := struct {
|
||||||
|
Include map[string]string
|
||||||
|
Exclude map[string]string
|
||||||
|
}{
|
||||||
|
Include: map[string]string{},
|
||||||
|
Exclude: map[string]string{},
|
||||||
|
}
|
||||||
|
|
||||||
|
out2 := map[string]string{}
|
||||||
|
|
||||||
|
unmarshal(&out1)
|
||||||
|
unmarshal(&out2)
|
||||||
|
|
||||||
|
c.Include = out1.Include
|
||||||
|
c.Exclude = out1.Exclude
|
||||||
|
for k, v := range out2 {
|
||||||
|
c.Include[k] = v
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
80
vendor/github.com/cncd/pipeline/pipeline/frontend/yaml/container.go
generated
vendored
Normal file
80
vendor/github.com/cncd/pipeline/pipeline/frontend/yaml/container.go
generated
vendored
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
package yaml
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
libcompose "github.com/docker/libcompose/yaml"
|
||||||
|
"gopkg.in/yaml.v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
// AuthConfig defines registry authentication credentials.
|
||||||
|
AuthConfig struct {
|
||||||
|
Username string
|
||||||
|
Password string
|
||||||
|
Email string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Containers denotes an ordered collection of containers.
|
||||||
|
Containers struct {
|
||||||
|
Containers []*Container
|
||||||
|
}
|
||||||
|
|
||||||
|
// Container defines a container.
|
||||||
|
Container struct {
|
||||||
|
AuthConfig AuthConfig `yaml:"auth_config,omitempty"`
|
||||||
|
CapAdd []string `yaml:"cap_add,omitempty"`
|
||||||
|
CapDrop []string `yaml:"cap_drop,omitempty"`
|
||||||
|
Command libcompose.Command `yaml:"command,omitempty"`
|
||||||
|
Commands libcompose.Stringorslice `yaml:"commands,omitempty"`
|
||||||
|
CPUQuota libcompose.StringorInt `yaml:"cpu_quota,omitempty"`
|
||||||
|
CPUSet string `yaml:"cpuset,omitempty"`
|
||||||
|
CPUShares libcompose.StringorInt `yaml:"cpu_shares,omitempty"`
|
||||||
|
Detached bool `yaml:"detach,omitempty"`
|
||||||
|
Devices []string `yaml:"devices,omitempty"`
|
||||||
|
DNS libcompose.Stringorslice `yaml:"dns,omitempty"`
|
||||||
|
DNSSearch libcompose.Stringorslice `yaml:"dns_search,omitempty"`
|
||||||
|
Entrypoint libcompose.Command `yaml:"entrypoint,omitempty"`
|
||||||
|
Environment libcompose.SliceorMap `yaml:"environment,omitempty"`
|
||||||
|
ExtraHosts []string `yaml:"extra_hosts,omitempty"`
|
||||||
|
Group string `yaml:"group,omitempty"`
|
||||||
|
Image string `yaml:"image,omitempty"`
|
||||||
|
Isolation string `yaml:"isolation,omitempty"`
|
||||||
|
Labels libcompose.SliceorMap `yaml:"labels,omitempty"`
|
||||||
|
MemLimit libcompose.MemStringorInt `yaml:"mem_limit,omitempty"`
|
||||||
|
MemSwapLimit libcompose.MemStringorInt `yaml:"memswap_limit,omitempty"`
|
||||||
|
MemSwappiness libcompose.MemStringorInt `yaml:"mem_swappiness,omitempty"`
|
||||||
|
Name string `yaml:"name,omitempty"`
|
||||||
|
NetworkMode string `yaml:"network_mode,omitempty"`
|
||||||
|
Networks libcompose.Networks `yaml:"networks,omitempty"`
|
||||||
|
Privileged bool `yaml:"privileged,omitempty"`
|
||||||
|
Pull bool `yaml:"pull,omitempty"`
|
||||||
|
ShmSize libcompose.MemStringorInt `yaml:"shm_size,omitempty"`
|
||||||
|
Ulimits libcompose.Ulimits `yaml:"ulimits,omitempty"`
|
||||||
|
Volumes libcompose.Volumes `yaml:"volumes,omitempty"`
|
||||||
|
Constraints Constraints `yaml:"when,omitempty"`
|
||||||
|
Vargs map[string]interface{} `yaml:",inline"`
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// UnmarshalYAML implements the Unmarshaller interface.
|
||||||
|
func (c *Containers) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||||
|
slice := yaml.MapSlice{}
|
||||||
|
if err := unmarshal(&slice); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, s := range slice {
|
||||||
|
container := Container{}
|
||||||
|
out, _ := yaml.Marshal(s.Value)
|
||||||
|
|
||||||
|
if err := yaml.Unmarshal(out, &container); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if container.Name == "" {
|
||||||
|
container.Name = fmt.Sprintf("%v", s.Key)
|
||||||
|
}
|
||||||
|
c.Containers = append(c.Containers, &container)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
109
vendor/github.com/cncd/pipeline/pipeline/frontend/yaml/linter/linter.go
generated
vendored
Normal file
109
vendor/github.com/cncd/pipeline/pipeline/frontend/yaml/linter/linter.go
generated
vendored
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
package linter
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/cncd/pipeline/pipeline/frontend/yaml"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A Linter lints a pipeline configuration.
|
||||||
|
type Linter struct {
|
||||||
|
trusted bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// New creates a new Linter with options.
|
||||||
|
func New(opts ...Option) *Linter {
|
||||||
|
linter := new(Linter)
|
||||||
|
for _, opt := range opts {
|
||||||
|
opt(linter)
|
||||||
|
}
|
||||||
|
return linter
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lint lints the configuration.
|
||||||
|
func (l *Linter) Lint(c *yaml.Config) error {
|
||||||
|
var containers []*yaml.Container
|
||||||
|
containers = append(containers, c.Pipeline.Containers...)
|
||||||
|
containers = append(containers, c.Services.Containers...)
|
||||||
|
|
||||||
|
for _, container := range containers {
|
||||||
|
if err := l.lintImage(container); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if l.trusted == false {
|
||||||
|
if err := l.lintTrusted(container); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if isService(container) == false {
|
||||||
|
if err := l.lintEntrypoint(container); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(c.Pipeline.Containers) == 0 {
|
||||||
|
return fmt.Errorf("Invalid or missing pipeline section")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Linter) lintImage(c *yaml.Container) error {
|
||||||
|
if len(c.Image) == 0 {
|
||||||
|
return fmt.Errorf("Invalid or missing image")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Linter) lintEntrypoint(c *yaml.Container) error {
|
||||||
|
if len(c.Entrypoint) != 0 {
|
||||||
|
return fmt.Errorf("Cannot override container entrypoint")
|
||||||
|
}
|
||||||
|
if len(c.Command) != 0 {
|
||||||
|
return fmt.Errorf("Cannot override container command")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Linter) lintTrusted(c *yaml.Container) error {
|
||||||
|
if c.Privileged {
|
||||||
|
return fmt.Errorf("Insufficient privileges to use privileged mode")
|
||||||
|
}
|
||||||
|
if c.ShmSize != 0 {
|
||||||
|
return fmt.Errorf("Insufficient privileges to override shm_size")
|
||||||
|
}
|
||||||
|
if len(c.DNS) != 0 {
|
||||||
|
return fmt.Errorf("Insufficient privileges to use custom dns")
|
||||||
|
}
|
||||||
|
if len(c.DNSSearch) != 0 {
|
||||||
|
return fmt.Errorf("Insufficient privileges to use dns_search")
|
||||||
|
}
|
||||||
|
if len(c.Devices) != 0 {
|
||||||
|
return fmt.Errorf("Insufficient privileges to use devices")
|
||||||
|
}
|
||||||
|
if len(c.ExtraHosts) != 0 {
|
||||||
|
return fmt.Errorf("Insufficient privileges to use extra_hosts")
|
||||||
|
}
|
||||||
|
if len(c.NetworkMode) != 0 {
|
||||||
|
return fmt.Errorf("Insufficient privileges to use network_mode")
|
||||||
|
}
|
||||||
|
if c.Networks.Networks != nil && len(c.Networks.Networks) != 0 {
|
||||||
|
return fmt.Errorf("Insufficient privileges to use networks")
|
||||||
|
}
|
||||||
|
if c.Volumes.Volumes != nil && len(c.Volumes.Volumes) != 0 {
|
||||||
|
return fmt.Errorf("Insufficient privileges to use volumes")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func isService(c *yaml.Container) bool {
|
||||||
|
return !isScript(c) && !isPlugin(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func isScript(c *yaml.Container) bool {
|
||||||
|
return len(c.Commands) != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func isPlugin(c *yaml.Container) bool {
|
||||||
|
return len(c.Vargs) != 0
|
||||||
|
}
|
11
vendor/github.com/cncd/pipeline/pipeline/frontend/yaml/linter/option.go
generated
vendored
Normal file
11
vendor/github.com/cncd/pipeline/pipeline/frontend/yaml/linter/option.go
generated
vendored
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
package linter
|
||||||
|
|
||||||
|
// Option configures a linting option.
|
||||||
|
type Option func(*Linter)
|
||||||
|
|
||||||
|
// WithTrusted adds the trusted option to the linter.
|
||||||
|
func WithTrusted(trusted bool) Option {
|
||||||
|
return func(linter *Linter) {
|
||||||
|
linter.trusted = trusted
|
||||||
|
}
|
||||||
|
}
|
117
vendor/github.com/cncd/pipeline/pipeline/frontend/yaml/matrix/matrix.go
generated
vendored
Normal file
117
vendor/github.com/cncd/pipeline/pipeline/frontend/yaml/matrix/matrix.go
generated
vendored
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
package matrix
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"gopkg.in/yaml.v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
limitTags = 10
|
||||||
|
limitAxis = 25
|
||||||
|
)
|
||||||
|
|
||||||
|
// Matrix represents the build matrix.
|
||||||
|
type Matrix map[string][]string
|
||||||
|
|
||||||
|
// Axis represents a single permutation of entries from the build matrix.
|
||||||
|
type Axis map[string]string
|
||||||
|
|
||||||
|
// String returns a string representation of an Axis as a comma-separated list
|
||||||
|
// of environment variables.
|
||||||
|
func (a Axis) String() string {
|
||||||
|
var envs []string
|
||||||
|
for k, v := range a {
|
||||||
|
envs = append(envs, k+"="+v)
|
||||||
|
}
|
||||||
|
return strings.Join(envs, " ")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse parses the Yaml matrix definition.
|
||||||
|
func Parse(data []byte) ([]Axis, error) {
|
||||||
|
|
||||||
|
axis, err := parseList(data)
|
||||||
|
if err == nil && len(axis) != 0 {
|
||||||
|
return axis, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
matrix, err := parse(data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// if not a matrix build return an array with just the single axis.
|
||||||
|
if len(matrix) == 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return calc(matrix), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseString parses the Yaml string matrix definition.
|
||||||
|
func ParseString(data string) ([]Axis, error) {
|
||||||
|
return Parse([]byte(data))
|
||||||
|
}
|
||||||
|
|
||||||
|
func calc(matrix Matrix) []Axis {
|
||||||
|
// calculate number of permutations and extract the list of tags
|
||||||
|
// (ie go_version, redis_version, etc)
|
||||||
|
var perm int
|
||||||
|
var tags []string
|
||||||
|
for k, v := range matrix {
|
||||||
|
perm *= len(v)
|
||||||
|
if perm == 0 {
|
||||||
|
perm = len(v)
|
||||||
|
}
|
||||||
|
tags = append(tags, k)
|
||||||
|
}
|
||||||
|
|
||||||
|
// structure to hold the transformed result set
|
||||||
|
axisList := []Axis{}
|
||||||
|
|
||||||
|
// for each axis calculate the uniqe set of values that should be used.
|
||||||
|
for p := 0; p < perm; p++ {
|
||||||
|
axis := map[string]string{}
|
||||||
|
decr := perm
|
||||||
|
for i, tag := range tags {
|
||||||
|
elems := matrix[tag]
|
||||||
|
decr = decr / len(elems)
|
||||||
|
elem := p / decr % len(elems)
|
||||||
|
axis[tag] = elems[elem]
|
||||||
|
|
||||||
|
// enforce a maximum number of tags in the build matrix.
|
||||||
|
if i > limitTags {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// append to the list of axis.
|
||||||
|
axisList = append(axisList, axis)
|
||||||
|
|
||||||
|
// enforce a maximum number of axis that should be calculated.
|
||||||
|
if p > limitAxis {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return axisList
|
||||||
|
}
|
||||||
|
|
||||||
|
func parse(raw []byte) (Matrix, error) {
|
||||||
|
data := struct {
|
||||||
|
Matrix map[string][]string
|
||||||
|
}{}
|
||||||
|
err := yaml.Unmarshal(raw, &data)
|
||||||
|
return data.Matrix, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseList(raw []byte) ([]Axis, error) {
|
||||||
|
data := struct {
|
||||||
|
Matrix struct {
|
||||||
|
Include []Axis
|
||||||
|
}
|
||||||
|
}{}
|
||||||
|
|
||||||
|
err := yaml.Unmarshal(raw, &data)
|
||||||
|
return data.Matrix.Include, err
|
||||||
|
}
|
48
vendor/github.com/cncd/pipeline/pipeline/frontend/yaml/network.go
generated
vendored
Normal file
48
vendor/github.com/cncd/pipeline/pipeline/frontend/yaml/network.go
generated
vendored
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
package yaml
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"gopkg.in/yaml.v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
// Networks defines a collection of networks.
|
||||||
|
Networks struct {
|
||||||
|
Networks []*Network
|
||||||
|
}
|
||||||
|
|
||||||
|
// Network defines a container network.
|
||||||
|
Network struct {
|
||||||
|
Name string `yaml:"name,omitempty"`
|
||||||
|
Driver string `yaml:"driver,omitempty"`
|
||||||
|
DriverOpts map[string]string `yaml:"driver_opts,omitempty"`
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// UnmarshalYAML implements the Unmarshaller interface.
|
||||||
|
func (n *Networks) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||||
|
slice := yaml.MapSlice{}
|
||||||
|
err := unmarshal(&slice)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, s := range slice {
|
||||||
|
nn := Network{}
|
||||||
|
out, _ := yaml.Marshal(s.Value)
|
||||||
|
|
||||||
|
err = yaml.Unmarshal(out, &nn)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if nn.Name == "" {
|
||||||
|
nn.Name = fmt.Sprintf("%v", s.Key)
|
||||||
|
}
|
||||||
|
if nn.Driver == "" {
|
||||||
|
nn.Driver = "bridge"
|
||||||
|
}
|
||||||
|
n.Networks = append(n.Networks, &nn)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
48
vendor/github.com/cncd/pipeline/pipeline/frontend/yaml/volume.go
generated
vendored
Normal file
48
vendor/github.com/cncd/pipeline/pipeline/frontend/yaml/volume.go
generated
vendored
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
package yaml
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"gopkg.in/yaml.v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
// Volumes defines a collection of volumes.
|
||||||
|
Volumes struct {
|
||||||
|
Volumes []*Volume
|
||||||
|
}
|
||||||
|
|
||||||
|
// Volume defines a container volume.
|
||||||
|
Volume struct {
|
||||||
|
Name string `yaml:"name,omitempty"`
|
||||||
|
Driver string `yaml:"driver,omitempty"`
|
||||||
|
DriverOpts map[string]string `yaml:"driver_opts,omitempty"`
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// UnmarshalYAML implements the Unmarshaller interface.
|
||||||
|
func (v *Volumes) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||||
|
slice := yaml.MapSlice{}
|
||||||
|
err := unmarshal(&slice)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, s := range slice {
|
||||||
|
vv := Volume{}
|
||||||
|
out, _ := yaml.Marshal(s.Value)
|
||||||
|
|
||||||
|
err = yaml.Unmarshal(out, &vv)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if vv.Name == "" {
|
||||||
|
vv.Name = fmt.Sprintf("%v", s.Key)
|
||||||
|
}
|
||||||
|
if vv.Driver == "" {
|
||||||
|
vv.Driver = "local"
|
||||||
|
}
|
||||||
|
v.Volumes = append(v.Volumes, &vv)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
36
vendor/github.com/cncd/pipeline/pipeline/interrupt/interrupt.go
generated
vendored
Normal file
36
vendor/github.com/cncd/pipeline/pipeline/interrupt/interrupt.go
generated
vendored
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
package interrupt
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
)
|
||||||
|
|
||||||
|
// WithContext returns a copy of parent context whose Done channel is closed
|
||||||
|
// when an os interrupt signal is received.
|
||||||
|
func WithContext(ctx context.Context) context.Context {
|
||||||
|
return WithContextFunc(ctx, func() {
|
||||||
|
println("ctrl+c received, terminating process")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithContextFunc returns a copy of parent context that is cancelled when
|
||||||
|
// an os interrupt signal is received. The callback function f is invoked
|
||||||
|
// before cancellation.
|
||||||
|
func WithContextFunc(ctx context.Context, f func()) context.Context {
|
||||||
|
ctx, cancel := context.WithCancel(ctx)
|
||||||
|
go func() {
|
||||||
|
c := make(chan os.Signal)
|
||||||
|
signal.Notify(c, os.Interrupt)
|
||||||
|
defer signal.Stop(c)
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
case <-c:
|
||||||
|
f()
|
||||||
|
cancel()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return ctx
|
||||||
|
}
|
20
vendor/github.com/cncd/pipeline/pipeline/logger.go
generated
vendored
Normal file
20
vendor/github.com/cncd/pipeline/pipeline/logger.go
generated
vendored
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
package pipeline
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/cncd/pipeline/pipeline/backend"
|
||||||
|
"github.com/cncd/pipeline/pipeline/multipart"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Logger handles the process logging.
|
||||||
|
type Logger interface {
|
||||||
|
Log(*backend.Step, multipart.Reader) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// LogFunc type is an adapter to allow the use of an ordinary
|
||||||
|
// function for process logging.
|
||||||
|
type LogFunc func(*backend.Step, multipart.Reader) error
|
||||||
|
|
||||||
|
// Log calls f(proc, r).
|
||||||
|
func (f LogFunc) Log(step *backend.Step, r multipart.Reader) error {
|
||||||
|
return f(step, r)
|
||||||
|
}
|
1
vendor/github.com/cncd/pipeline/pipeline/multipart/doc.go
generated
vendored
Normal file
1
vendor/github.com/cncd/pipeline/pipeline/multipart/doc.go
generated
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
package multipart
|
103
vendor/github.com/cncd/pipeline/pipeline/multipart/reader.go
generated
vendored
Normal file
103
vendor/github.com/cncd/pipeline/pipeline/multipart/reader.go
generated
vendored
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
package multipart
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"bytes"
|
||||||
|
"io"
|
||||||
|
"mime/multipart"
|
||||||
|
"net/textproto"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
// Reader is an iterator over parts in a multipart log stream.
|
||||||
|
Reader interface {
|
||||||
|
// NextPart returns the next part in the multipart or
|
||||||
|
// an error. When there are no more parts, the error
|
||||||
|
// io.EOF is returned.
|
||||||
|
NextPart() (Part, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Part represents a single part in a multipart body.
|
||||||
|
Part interface {
|
||||||
|
io.Reader
|
||||||
|
|
||||||
|
// Header returns the headers of the body with the
|
||||||
|
// keys canonicalized.
|
||||||
|
Header() textproto.MIMEHeader
|
||||||
|
|
||||||
|
// FileName returns the filename parameter of the
|
||||||
|
// Content-Disposition header.
|
||||||
|
FileName() string
|
||||||
|
|
||||||
|
// FormName returns the name parameter if p has a
|
||||||
|
// Content-Disposition of type form-data.
|
||||||
|
FormName() string
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// New returns a new multipart Reader.
|
||||||
|
func New(r io.Reader) Reader {
|
||||||
|
buf := bufio.NewReader(r)
|
||||||
|
out, _ := buf.Peek(8)
|
||||||
|
|
||||||
|
if bytes.Equal(out, []byte("PIPELINE")) {
|
||||||
|
return &multipartReader{
|
||||||
|
reader: multipart.NewReader(buf, "boundary"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &textReader{
|
||||||
|
reader: buf,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// wraps the stdlib multi-part reader
|
||||||
|
//
|
||||||
|
|
||||||
|
type multipartReader struct {
|
||||||
|
reader *multipart.Reader
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *multipartReader) NextPart() (Part, error) {
|
||||||
|
next, err := r.reader.NextPart()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
part := new(part)
|
||||||
|
part.Reader = next
|
||||||
|
part.filename = next.FileName()
|
||||||
|
part.formname = next.FormName()
|
||||||
|
part.header = next.Header
|
||||||
|
return part, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// wraps a simple io.Reader to satisfy the multi-part interface
|
||||||
|
//
|
||||||
|
|
||||||
|
type textReader struct {
|
||||||
|
reader io.Reader
|
||||||
|
done bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *textReader) NextPart() (Part, error) {
|
||||||
|
if r.done {
|
||||||
|
return nil, io.EOF
|
||||||
|
}
|
||||||
|
r.done = true
|
||||||
|
p := new(part)
|
||||||
|
p.Reader = r.reader
|
||||||
|
return p, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type part struct {
|
||||||
|
io.Reader
|
||||||
|
|
||||||
|
filename string
|
||||||
|
formname string
|
||||||
|
header textproto.MIMEHeader
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *part) Header() textproto.MIMEHeader { return p.header }
|
||||||
|
func (p *part) FileName() string { return p.filename }
|
||||||
|
func (p *part) FormName() string { return p.filename }
|
38
vendor/github.com/cncd/pipeline/pipeline/option.go
generated
vendored
Normal file
38
vendor/github.com/cncd/pipeline/pipeline/option.go
generated
vendored
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
package pipeline
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/cncd/pipeline/pipeline/backend"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Option configures a runtime option.
|
||||||
|
type Option func(*Runtime)
|
||||||
|
|
||||||
|
// WithEngine returns an option configured with a runtime engine.
|
||||||
|
func WithEngine(engine backend.Engine) Option {
|
||||||
|
return func(r *Runtime) {
|
||||||
|
r.engine = engine
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithLogger returns an option configured with a runtime logger.
|
||||||
|
func WithLogger(logger Logger) Option {
|
||||||
|
return func(r *Runtime) {
|
||||||
|
r.logger = logger
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithTracer returns an option configured with a runtime tracer.
|
||||||
|
func WithTracer(tracer Tracer) Option {
|
||||||
|
return func(r *Runtime) {
|
||||||
|
r.tracer = tracer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithContext returns an option configured with a context.
|
||||||
|
func WithContext(ctx context.Context) Option {
|
||||||
|
return func(r *Runtime) {
|
||||||
|
r.ctx = ctx
|
||||||
|
}
|
||||||
|
}
|
37
vendor/github.com/cncd/pipeline/pipeline/parse.go
generated
vendored
Normal file
37
vendor/github.com/cncd/pipeline/pipeline/parse.go
generated
vendored
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
package pipeline
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/cncd/pipeline/pipeline/backend"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Parse parses the pipeline config from an io.Reader.
|
||||||
|
func Parse(r io.Reader) (*backend.Config, error) {
|
||||||
|
cfg := new(backend.Config)
|
||||||
|
err := json.NewDecoder(r).Decode(cfg)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return cfg, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseFile parses the pipeline config from a file.
|
||||||
|
func ParseFile(path string) (*backend.Config, error) {
|
||||||
|
f, err := os.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
return Parse(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseString parses the pipeline config from a string.
|
||||||
|
func ParseString(s string) (*backend.Config, error) {
|
||||||
|
return Parse(
|
||||||
|
strings.NewReader(s),
|
||||||
|
)
|
||||||
|
}
|
175
vendor/github.com/cncd/pipeline/pipeline/pipeline.go
generated
vendored
Normal file
175
vendor/github.com/cncd/pipeline/pipeline/pipeline.go
generated
vendored
Normal file
@ -0,0 +1,175 @@
|
|||||||
|
package pipeline
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"golang.org/x/sync/errgroup"
|
||||||
|
|
||||||
|
"github.com/cncd/pipeline/pipeline/backend"
|
||||||
|
"github.com/cncd/pipeline/pipeline/multipart"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
// State defines the pipeline and process state.
|
||||||
|
State struct {
|
||||||
|
// Global state of the pipeline.
|
||||||
|
Pipeline struct {
|
||||||
|
// Pipeline time started
|
||||||
|
Time int64 `json:"time"`
|
||||||
|
// Current pipeline step
|
||||||
|
Step *backend.Step `json:"step"`
|
||||||
|
// Current pipeline error state
|
||||||
|
Error error `json:"error"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Current process state.
|
||||||
|
Process *backend.State
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// Runtime is a configuration runtime.
|
||||||
|
type Runtime struct {
|
||||||
|
err error
|
||||||
|
spec *backend.Config
|
||||||
|
engine backend.Engine
|
||||||
|
started int64
|
||||||
|
|
||||||
|
ctx context.Context
|
||||||
|
tracer Tracer
|
||||||
|
logger Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
// New returns a new runtime using the specified runtime
|
||||||
|
// configuration and runtime engine.
|
||||||
|
func New(spec *backend.Config, opts ...Option) *Runtime {
|
||||||
|
r := new(Runtime)
|
||||||
|
r.spec = spec
|
||||||
|
r.ctx = context.Background()
|
||||||
|
for _, opts := range opts {
|
||||||
|
opts(r)
|
||||||
|
}
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run starts the runtime and waits for it to complete.
|
||||||
|
func (r *Runtime) Run() error {
|
||||||
|
defer func() {
|
||||||
|
r.engine.Destroy(r.spec)
|
||||||
|
}()
|
||||||
|
|
||||||
|
r.started = time.Now().Unix()
|
||||||
|
if err := r.engine.Setup(r.spec); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, stage := range r.spec.Stages {
|
||||||
|
select {
|
||||||
|
case <-r.ctx.Done():
|
||||||
|
return ErrCancel
|
||||||
|
case err := <-r.execAll(stage.Steps):
|
||||||
|
if err != nil {
|
||||||
|
r.err = err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r.err
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
|
||||||
|
func (r *Runtime) execAll(procs []*backend.Step) <-chan error {
|
||||||
|
var g errgroup.Group
|
||||||
|
done := make(chan error)
|
||||||
|
|
||||||
|
for _, proc := range procs {
|
||||||
|
proc := proc
|
||||||
|
g.Go(func() error {
|
||||||
|
return r.exec(proc)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
done <- g.Wait()
|
||||||
|
close(done)
|
||||||
|
}()
|
||||||
|
return done
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
|
||||||
|
func (r *Runtime) exec(proc *backend.Step) error {
|
||||||
|
switch {
|
||||||
|
case r.err != nil && proc.OnFailure == false:
|
||||||
|
return nil
|
||||||
|
case r.err == nil && proc.OnSuccess == false:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.tracer != nil {
|
||||||
|
state := new(State)
|
||||||
|
state.Pipeline.Time = r.started
|
||||||
|
state.Pipeline.Error = r.err
|
||||||
|
state.Pipeline.Step = proc
|
||||||
|
state.Process = new(backend.State) // empty
|
||||||
|
if err := r.tracer.Trace(state); err == ErrSkip {
|
||||||
|
return nil
|
||||||
|
} else if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := r.engine.Exec(proc); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.logger != nil {
|
||||||
|
rc, err := r.engine.Tail(proc)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
r.logger.Log(proc, multipart.New(rc))
|
||||||
|
rc.Close()
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
if proc.Detached {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
wait, err := r.engine.Wait(proc)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.tracer != nil {
|
||||||
|
state := new(State)
|
||||||
|
state.Pipeline.Time = r.started
|
||||||
|
state.Pipeline.Error = r.err
|
||||||
|
state.Pipeline.Step = proc
|
||||||
|
state.Process = wait
|
||||||
|
if err := r.tracer.Trace(state); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if wait.OOMKilled {
|
||||||
|
return &OomError{
|
||||||
|
Name: proc.Name,
|
||||||
|
Code: wait.ExitCode,
|
||||||
|
}
|
||||||
|
} else if wait.ExitCode != 0 {
|
||||||
|
return &ExitError{
|
||||||
|
Name: proc.Name,
|
||||||
|
Code: wait.ExitCode,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
188
vendor/github.com/cncd/pipeline/pipeline/rpc/client.go
generated
vendored
Normal file
188
vendor/github.com/cncd/pipeline/pipeline/rpc/client.go
generated
vendored
Normal file
@ -0,0 +1,188 @@
|
|||||||
|
package rpc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"math"
|
||||||
|
"net/http"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/gorilla/websocket"
|
||||||
|
"github.com/sourcegraph/jsonrpc2"
|
||||||
|
websocketrpc "github.com/sourcegraph/jsonrpc2/websocket"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
methodNext = "next"
|
||||||
|
methodWait = "wait"
|
||||||
|
methodDone = "done"
|
||||||
|
methodExtend = "extend"
|
||||||
|
methodUpdate = "update"
|
||||||
|
methodUpload = "upload"
|
||||||
|
methodLog = "log"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
uploadReq struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Mime string `json:"mime"`
|
||||||
|
Data []byte `json:"data"`
|
||||||
|
}
|
||||||
|
|
||||||
|
updateReq struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
State State `json:"state"`
|
||||||
|
}
|
||||||
|
|
||||||
|
logReq struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Line *Line `json:"line"`
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
defaultRetryClount = math.MaxInt32
|
||||||
|
defaultBackoff = 10 * time.Second
|
||||||
|
)
|
||||||
|
|
||||||
|
// Client represents an rpc client.
|
||||||
|
type Client struct {
|
||||||
|
sync.Mutex
|
||||||
|
|
||||||
|
conn *jsonrpc2.Conn
|
||||||
|
done bool
|
||||||
|
retry int
|
||||||
|
backoff time.Duration
|
||||||
|
endpoint string
|
||||||
|
token string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewClient returns a new Client.
|
||||||
|
func NewClient(endpoint string, opts ...Option) (*Client, error) {
|
||||||
|
cli := &Client{
|
||||||
|
endpoint: endpoint,
|
||||||
|
retry: defaultRetryClount,
|
||||||
|
backoff: defaultBackoff,
|
||||||
|
}
|
||||||
|
for _, opt := range opts {
|
||||||
|
opt(cli)
|
||||||
|
}
|
||||||
|
err := cli.openRetry()
|
||||||
|
return cli, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next returns the next pipeline in the queue.
|
||||||
|
func (t *Client) Next(c context.Context, f Filter) (*Pipeline, error) {
|
||||||
|
res := new(Pipeline)
|
||||||
|
err := t.call(c, methodNext, f, res)
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait blocks until the pipeline is complete.
|
||||||
|
func (t *Client) Wait(c context.Context, id string) error {
|
||||||
|
// err := t.call(c, methodWait, id, nil)
|
||||||
|
// if err != nil && err.Error() == ErrCancelled.Error() {
|
||||||
|
// return ErrCancelled
|
||||||
|
// }
|
||||||
|
return t.call(c, methodWait, id, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Done signals the pipeline is complete.
|
||||||
|
func (t *Client) Done(c context.Context, id string) error {
|
||||||
|
return t.call(c, methodDone, id, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extend extends the pipeline deadline.
|
||||||
|
func (t *Client) Extend(c context.Context, id string) error {
|
||||||
|
return t.call(c, methodExtend, id, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update updates the pipeline state.
|
||||||
|
func (t *Client) Update(c context.Context, id string, state State) error {
|
||||||
|
params := updateReq{id, state}
|
||||||
|
return t.call(c, methodUpdate, ¶ms, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log writes the pipeline log entry.
|
||||||
|
func (t *Client) Log(c context.Context, id string, line *Line) error {
|
||||||
|
params := logReq{id, line}
|
||||||
|
return t.call(c, methodLog, ¶ms, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Upload uploads the pipeline artifact.
|
||||||
|
func (t *Client) Upload(c context.Context, id, mime string, file io.Reader) error {
|
||||||
|
data, err := ioutil.ReadAll(file)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
params := uploadReq{id, mime, data}
|
||||||
|
return t.call(c, methodUpload, params, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close closes the client connection.
|
||||||
|
func (t *Client) Close() error {
|
||||||
|
t.Lock()
|
||||||
|
t.done = true
|
||||||
|
t.Unlock()
|
||||||
|
return t.conn.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// call makes the remote prodedure call. If the call fails due to connectivity
|
||||||
|
// issues the connection is re-establish and call re-attempted.
|
||||||
|
func (t *Client) call(ctx context.Context, name string, req, res interface{}) error {
|
||||||
|
if err := t.conn.Call(ctx, name, req, res); err == nil {
|
||||||
|
return nil
|
||||||
|
} else if err != jsonrpc2.ErrClosed && err != io.ErrUnexpectedEOF {
|
||||||
|
log.Printf("rpc: error making call: %s", err)
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
log.Printf("rpc: error making call: connection closed: %s", err)
|
||||||
|
}
|
||||||
|
if err := t.openRetry(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return t.conn.Call(ctx, name, req, res)
|
||||||
|
}
|
||||||
|
|
||||||
|
// openRetry opens the connection and will retry on failure until
|
||||||
|
// the connection is successfully open, or the maximum retry count
|
||||||
|
// is exceeded.
|
||||||
|
func (t *Client) openRetry() error {
|
||||||
|
for i := 0; i < t.retry; i++ {
|
||||||
|
err := t.open()
|
||||||
|
if err == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if err == io.EOF {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("rpc: error re-connecting: %s", err)
|
||||||
|
<-time.After(t.backoff)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// open creates a websocket connection to a peer and establishes a json
|
||||||
|
// rpc communication stream.
|
||||||
|
func (t *Client) open() error {
|
||||||
|
t.Lock()
|
||||||
|
defer t.Unlock()
|
||||||
|
if t.done {
|
||||||
|
return io.EOF
|
||||||
|
}
|
||||||
|
header := map[string][]string{
|
||||||
|
"Content-Type": {"application/json-rpc"},
|
||||||
|
"Authorization": {"Bearer " + t.token},
|
||||||
|
}
|
||||||
|
conn, _, err := websocket.DefaultDialer.Dial(t.endpoint, http.Header(header))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
stream := websocketrpc.NewObjectStream(conn)
|
||||||
|
t.conn = jsonrpc2.NewConn(context.Background(), stream, nil)
|
||||||
|
return nil
|
||||||
|
}
|
95
vendor/github.com/cncd/pipeline/pipeline/rpc/line.go
generated
vendored
Normal file
95
vendor/github.com/cncd/pipeline/pipeline/rpc/line.go
generated
vendored
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
package rpc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Identifies the type of line in the logs.
|
||||||
|
const (
|
||||||
|
LineStdout int = iota
|
||||||
|
LineStderr
|
||||||
|
LineExitCode
|
||||||
|
LineMetadata
|
||||||
|
LineProgress
|
||||||
|
)
|
||||||
|
|
||||||
|
// Line is a line of console output.
|
||||||
|
type Line struct {
|
||||||
|
Proc string `json:"proc,omitempty"`
|
||||||
|
Time int64 `json:"time,omitempty"`
|
||||||
|
Type int `json:"type,omitempty"`
|
||||||
|
Pos int `json:"pos,omityempty"`
|
||||||
|
Out string `json:"out,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Line) String() string {
|
||||||
|
switch l.Type {
|
||||||
|
case LineExitCode:
|
||||||
|
return fmt.Sprintf("[%s] exit code %s", l.Proc, l.Out)
|
||||||
|
default:
|
||||||
|
return fmt.Sprintf("[%s:L%v:%vs] %s", l.Proc, l.Pos, l.Time, l.Out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// LineWriter sends logs to the client.
|
||||||
|
type LineWriter struct {
|
||||||
|
peer Peer
|
||||||
|
id string
|
||||||
|
name string
|
||||||
|
num int
|
||||||
|
now time.Time
|
||||||
|
rep *strings.Replacer
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewLineWriter returns a new line reader.
|
||||||
|
func NewLineWriter(peer Peer, id, name string, secret ...string) *LineWriter {
|
||||||
|
w := new(LineWriter)
|
||||||
|
w.peer = peer
|
||||||
|
w.id = id
|
||||||
|
w.name = name
|
||||||
|
w.num = 0
|
||||||
|
w.now = time.Now().UTC()
|
||||||
|
|
||||||
|
var oldnew []string
|
||||||
|
for _, old := range secret {
|
||||||
|
oldnew = append(oldnew, old)
|
||||||
|
oldnew = append(oldnew, "********")
|
||||||
|
}
|
||||||
|
if len(oldnew) != 0 {
|
||||||
|
w.rep = strings.NewReplacer(oldnew...)
|
||||||
|
}
|
||||||
|
return w
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *LineWriter) Write(p []byte) (n int, err error) {
|
||||||
|
out := string(p)
|
||||||
|
if w.rep != nil {
|
||||||
|
out = w.rep.Replace(out)
|
||||||
|
}
|
||||||
|
|
||||||
|
line := &Line{
|
||||||
|
Out: out,
|
||||||
|
Proc: w.name,
|
||||||
|
Pos: w.num,
|
||||||
|
Time: int64(time.Since(w.now).Seconds()),
|
||||||
|
Type: LineStdout,
|
||||||
|
}
|
||||||
|
w.peer.Log(context.Background(), w.id, line)
|
||||||
|
w.num++
|
||||||
|
|
||||||
|
// for _, part := range bytes.Split(p, []byte{'\n'}) {
|
||||||
|
// line := &Line{
|
||||||
|
// Out: string(part),
|
||||||
|
// Proc: w.name,
|
||||||
|
// Pos: w.num,
|
||||||
|
// Time: int64(time.Since(w.now).Seconds()),
|
||||||
|
// Type: LineStdout,
|
||||||
|
// }
|
||||||
|
// w.peer.Log(context.Background(), w.id, line)
|
||||||
|
// w.num++
|
||||||
|
// }
|
||||||
|
return len(p), nil
|
||||||
|
}
|
29
vendor/github.com/cncd/pipeline/pipeline/rpc/option.go
generated
vendored
Normal file
29
vendor/github.com/cncd/pipeline/pipeline/rpc/option.go
generated
vendored
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
package rpc
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
// Option configures a client option.
|
||||||
|
type Option func(*Client)
|
||||||
|
|
||||||
|
// WithBackoff configures the backoff duration when attempting
|
||||||
|
// to re-connect to a server.
|
||||||
|
func WithBackoff(d time.Duration) Option {
|
||||||
|
return func(c *Client) {
|
||||||
|
c.backoff = d
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithRetryLimit configures the maximum number of retries when
|
||||||
|
// connecting to the server.
|
||||||
|
func WithRetryLimit(i int) Option {
|
||||||
|
return func(c *Client) {
|
||||||
|
c.retry = i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithToken configures the client authorization token.
|
||||||
|
func WithToken(t string) Option {
|
||||||
|
return func(c *Client) {
|
||||||
|
c.token = t
|
||||||
|
}
|
||||||
|
}
|
63
vendor/github.com/cncd/pipeline/pipeline/rpc/peer.go
generated
vendored
Normal file
63
vendor/github.com/cncd/pipeline/pipeline/rpc/peer.go
generated
vendored
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
package rpc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"github.com/cncd/pipeline/pipeline/backend"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ErrCancelled signals the pipeine is cancelled.
|
||||||
|
// var ErrCancelled = errors.New("cancelled")
|
||||||
|
|
||||||
|
type (
|
||||||
|
// Filter defines filters for fetching items from the queue.
|
||||||
|
Filter struct {
|
||||||
|
Labels map[string]string `json:"labels"`
|
||||||
|
Expr string `json:"expr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// State defines the pipeline state.
|
||||||
|
State struct {
|
||||||
|
Proc string `json:"proc"`
|
||||||
|
Exited bool `json:"exited"`
|
||||||
|
ExitCode int `json:"exit_code"`
|
||||||
|
Started int64 `json:"started"`
|
||||||
|
Finished int64 `json:"finished"`
|
||||||
|
Error string `json:"error"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pipeline defines the pipeline execution details.
|
||||||
|
Pipeline struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Config *backend.Config `json:"config"`
|
||||||
|
Timeout int64 `json:"timeout"`
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// NoFilter is an empty filter.
|
||||||
|
var NoFilter = Filter{}
|
||||||
|
|
||||||
|
// Peer defines a peer-to-peer connection.
|
||||||
|
type Peer interface {
|
||||||
|
// Next returns the next pipeline in the queue.
|
||||||
|
Next(c context.Context, f Filter) (*Pipeline, error)
|
||||||
|
|
||||||
|
// Wait blocks untilthe pipeline is complete.
|
||||||
|
Wait(c context.Context, id string) error
|
||||||
|
|
||||||
|
// Done signals the pipeline is complete.
|
||||||
|
Done(c context.Context, id string) error
|
||||||
|
|
||||||
|
// Extend extends the pipeline deadline
|
||||||
|
Extend(c context.Context, id string) error
|
||||||
|
|
||||||
|
// Update updates the pipeline state.
|
||||||
|
Update(c context.Context, id string, state State) error
|
||||||
|
|
||||||
|
// Upload uploads the pipeline artifact.
|
||||||
|
Upload(c context.Context, id, mime string, file io.Reader) error
|
||||||
|
|
||||||
|
// Log writes the pipeline log entry.
|
||||||
|
Log(c context.Context, id string, line *Line) error
|
||||||
|
}
|
141
vendor/github.com/cncd/pipeline/pipeline/rpc/server.go
generated
vendored
Normal file
141
vendor/github.com/cncd/pipeline/pipeline/rpc/server.go
generated
vendored
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
package rpc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/gorilla/websocket"
|
||||||
|
"github.com/sourcegraph/jsonrpc2"
|
||||||
|
websocketrpc "github.com/sourcegraph/jsonrpc2/websocket"
|
||||||
|
)
|
||||||
|
|
||||||
|
// errNoSuchMethod is returned when the name rpc method does not exist.
|
||||||
|
var errNoSuchMethod = errors.New("No such rpc method")
|
||||||
|
|
||||||
|
// noContext is an empty context used when no context is required.
|
||||||
|
var noContext = context.Background()
|
||||||
|
|
||||||
|
// Server represents an rpc server.
|
||||||
|
type Server struct {
|
||||||
|
peer Peer
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewServer returns an rpc Server.
|
||||||
|
func NewServer(peer Peer) *Server {
|
||||||
|
return &Server{peer}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServeHTTP implements an http.Handler that answers rpc requests.
|
||||||
|
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
|
upgrader := websocket.Upgrader{}
|
||||||
|
c, err := upgrader.Upgrade(w, r, nil)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
conn := jsonrpc2.NewConn(ctx,
|
||||||
|
websocketrpc.NewObjectStream(c),
|
||||||
|
jsonrpc2.HandlerWithError(s.router),
|
||||||
|
)
|
||||||
|
defer func() {
|
||||||
|
cancel()
|
||||||
|
conn.Close()
|
||||||
|
}()
|
||||||
|
<-conn.DisconnectNotify()
|
||||||
|
}
|
||||||
|
|
||||||
|
// router implements an jsonrpc2.Handler that answers RPC requests.
|
||||||
|
func (s *Server) router(ctx context.Context, conn *jsonrpc2.Conn, req *jsonrpc2.Request) (interface{}, error) {
|
||||||
|
switch req.Method {
|
||||||
|
case methodNext:
|
||||||
|
return s.next(ctx, req)
|
||||||
|
case methodWait:
|
||||||
|
return s.wait(ctx, req)
|
||||||
|
case methodDone:
|
||||||
|
return s.done(ctx, req)
|
||||||
|
case methodExtend:
|
||||||
|
return s.extend(ctx, req)
|
||||||
|
case methodUpdate:
|
||||||
|
return s.update(req)
|
||||||
|
case methodLog:
|
||||||
|
return s.log(req)
|
||||||
|
case methodUpload:
|
||||||
|
return s.upload(req)
|
||||||
|
default:
|
||||||
|
return nil, errNoSuchMethod
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// next unmarshals the rpc request parameters and invokes the peer.Next
|
||||||
|
// procedure. The results are retuned and written to the rpc response.
|
||||||
|
func (s *Server) next(ctx context.Context, req *jsonrpc2.Request) (interface{}, error) {
|
||||||
|
in := Filter{}
|
||||||
|
if err := json.Unmarshal([]byte(*req.Params), &in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return s.peer.Next(ctx, in)
|
||||||
|
}
|
||||||
|
|
||||||
|
// wait unmarshals the rpc request parameters and invokes the peer.Wait
|
||||||
|
// procedure. The results are retuned and written to the rpc response.
|
||||||
|
func (s *Server) wait(ctx context.Context, req *jsonrpc2.Request) (interface{}, error) {
|
||||||
|
var id string
|
||||||
|
err := json.Unmarshal([]byte(*req.Params), &id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return nil, s.peer.Wait(ctx, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
// done unmarshals the rpc request parameters and invokes the peer.Done
|
||||||
|
// procedure. The results are retuned and written to the rpc response.
|
||||||
|
func (s *Server) done(ctx context.Context, req *jsonrpc2.Request) (interface{}, error) {
|
||||||
|
var id string
|
||||||
|
err := json.Unmarshal([]byte(*req.Params), &id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return nil, s.peer.Done(ctx, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
// extend unmarshals the rpc request parameters and invokes the peer.Extend
|
||||||
|
// procedure. The results are retuned and written to the rpc response.
|
||||||
|
func (s *Server) extend(ctx context.Context, req *jsonrpc2.Request) (interface{}, error) {
|
||||||
|
var id string
|
||||||
|
err := json.Unmarshal([]byte(*req.Params), &id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return nil, s.peer.Extend(ctx, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
// update unmarshals the rpc request parameters and invokes the peer.Update
|
||||||
|
// procedure. The results are retuned and written to the rpc response.
|
||||||
|
func (s *Server) update(req *jsonrpc2.Request) (interface{}, error) {
|
||||||
|
in := new(updateReq)
|
||||||
|
if err := json.Unmarshal([]byte(*req.Params), in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return nil, s.peer.Update(noContext, in.ID, in.State)
|
||||||
|
}
|
||||||
|
|
||||||
|
// log unmarshals the rpc request parameters and invokes the peer.Log
|
||||||
|
// procedure. The results are retuned and written to the rpc response.
|
||||||
|
func (s *Server) log(req *jsonrpc2.Request) (interface{}, error) {
|
||||||
|
in := new(logReq)
|
||||||
|
if err := json.Unmarshal([]byte(*req.Params), in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return nil, s.peer.Log(noContext, in.ID, in.Line)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) upload(req *jsonrpc2.Request) (interface{}, error) {
|
||||||
|
in := new(uploadReq)
|
||||||
|
if err := json.Unmarshal([]byte(*req.Params), in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return nil, s.peer.Upload(noContext, in.ID, in.Mime, bytes.NewBuffer(in.Data))
|
||||||
|
}
|
45
vendor/github.com/cncd/pipeline/pipeline/tracer.go
generated
vendored
Normal file
45
vendor/github.com/cncd/pipeline/pipeline/tracer.go
generated
vendored
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
package pipeline
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Tracer handles process tracing.
|
||||||
|
type Tracer interface {
|
||||||
|
Trace(*State) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// TraceFunc type is an adapter to allow the use of ordinary
|
||||||
|
// functions as a Tracer.
|
||||||
|
type TraceFunc func(*State) error
|
||||||
|
|
||||||
|
// Trace calls f(proc, state).
|
||||||
|
func (f TraceFunc) Trace(state *State) error {
|
||||||
|
return f(state)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultTracer provides a tracer that updates the CI_ enviornment
|
||||||
|
// variables to include the correct timestamp and status.
|
||||||
|
// TODO(bradrydzewski) find either a new home or better name for this.
|
||||||
|
var DefaultTracer = TraceFunc(func(state *State) error {
|
||||||
|
if state.Process.Exited {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if state.Pipeline.Step.Environment == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
state.Pipeline.Step.Environment["CI_BUILD_STATUS"] = "success"
|
||||||
|
state.Pipeline.Step.Environment["CI_BUILD_STARTED"] = strconv.FormatInt(state.Pipeline.Time, 10)
|
||||||
|
state.Pipeline.Step.Environment["CI_BUILD_FINISHED"] = strconv.FormatInt(time.Now().Unix(), 10)
|
||||||
|
|
||||||
|
state.Pipeline.Step.Environment["CI_JOB_STATUS"] = "success"
|
||||||
|
state.Pipeline.Step.Environment["CI_JOB_STARTED"] = strconv.FormatInt(state.Pipeline.Time, 10)
|
||||||
|
state.Pipeline.Step.Environment["CI_JOB_FINISHED"] = strconv.FormatInt(time.Now().Unix(), 10)
|
||||||
|
|
||||||
|
if state.Pipeline.Error != nil {
|
||||||
|
state.Pipeline.Step.Environment["CI_BUILD_STATUS"] = "failure"
|
||||||
|
state.Pipeline.Step.Environment["CI_JOB_STATUS"] = "failure"
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
29
vendor/github.com/cncd/pubsub/LICENSE
generated
vendored
Normal file
29
vendor/github.com/cncd/pubsub/LICENSE
generated
vendored
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
BSD 3-Clause License
|
||||||
|
|
||||||
|
Copyright (c) 2017, Brad Rydzewski
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright notice, this
|
||||||
|
list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
* Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation
|
||||||
|
and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
* Neither the name of the copyright holder nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived from
|
||||||
|
this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
6
vendor/github.com/cncd/pubsub/README
generated
vendored
Normal file
6
vendor/github.com/cncd/pubsub/README
generated
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
Go package provides a common interface for publish-subscriber messaging.
|
||||||
|
|
||||||
|
Documentation:
|
||||||
|
|
||||||
|
http://godoc.org/github.com/cncd/pubsub
|
||||||
|
http://godoc.org/github.com/cncd/pubsub/gcp
|
75
vendor/github.com/cncd/pubsub/pub.go
generated
vendored
Normal file
75
vendor/github.com/cncd/pubsub/pub.go
generated
vendored
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
package pubsub
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
type subscriber struct {
|
||||||
|
receiver Receiver
|
||||||
|
}
|
||||||
|
|
||||||
|
type publisher struct {
|
||||||
|
sync.Mutex
|
||||||
|
|
||||||
|
topics map[string]*topic
|
||||||
|
}
|
||||||
|
|
||||||
|
// New creates an in-memory publisher.
|
||||||
|
func New() Publisher {
|
||||||
|
return &publisher{
|
||||||
|
topics: make(map[string]*topic),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *publisher) Create(c context.Context, dest string) error {
|
||||||
|
p.Lock()
|
||||||
|
t, ok := p.topics[dest]
|
||||||
|
if !ok {
|
||||||
|
t = newTopic(dest)
|
||||||
|
p.topics[dest] = t
|
||||||
|
}
|
||||||
|
p.Unlock()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *publisher) Publish(c context.Context, dest string, message Message) error {
|
||||||
|
p.Lock()
|
||||||
|
t, ok := p.topics[dest]
|
||||||
|
p.Unlock()
|
||||||
|
if !ok {
|
||||||
|
return ErrNotFound
|
||||||
|
}
|
||||||
|
t.publish(message)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *publisher) Subscribe(c context.Context, dest string, receiver Receiver) error {
|
||||||
|
p.Lock()
|
||||||
|
t, ok := p.topics[dest]
|
||||||
|
p.Unlock()
|
||||||
|
if !ok {
|
||||||
|
return ErrNotFound
|
||||||
|
}
|
||||||
|
s := &subscriber{
|
||||||
|
receiver: receiver,
|
||||||
|
}
|
||||||
|
t.subscribe(s)
|
||||||
|
select {
|
||||||
|
case <-c.Done():
|
||||||
|
case <-t.done:
|
||||||
|
}
|
||||||
|
t.unsubscribe(s)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *publisher) Remove(c context.Context, dest string) error {
|
||||||
|
p.Lock()
|
||||||
|
t, ok := p.topics[dest]
|
||||||
|
if ok {
|
||||||
|
delete(p.topics, dest)
|
||||||
|
t.close()
|
||||||
|
}
|
||||||
|
p.Unlock()
|
||||||
|
return nil
|
||||||
|
}
|
71
vendor/github.com/cncd/pubsub/pubsub.go
generated
vendored
Normal file
71
vendor/github.com/cncd/pubsub/pubsub.go
generated
vendored
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
// Package pubsub implements a publish-subscriber messaging system.
|
||||||
|
package pubsub
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ErrNotFound is returned when the named topic does not exist.
|
||||||
|
var ErrNotFound = errors.New("topic not found")
|
||||||
|
|
||||||
|
// Message defines a published message.
|
||||||
|
type Message struct {
|
||||||
|
// ID identifies this message.
|
||||||
|
ID string `json:"id,omitempty"`
|
||||||
|
|
||||||
|
// Data is the actual data in the entry.
|
||||||
|
Data []byte `json:"data"`
|
||||||
|
|
||||||
|
// Labels represents the key-value pairs the entry is lebeled with.
|
||||||
|
Labels map[string]string `json:"labels,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Receiver receives published messages.
|
||||||
|
type Receiver func(Message)
|
||||||
|
|
||||||
|
// Publisher defines a mechanism for communicating messages from a group
|
||||||
|
// of senders, called producers, to a group of consumers.
|
||||||
|
type Publisher interface {
|
||||||
|
// Create creates the named topic.
|
||||||
|
Create(c context.Context, topic string) error
|
||||||
|
|
||||||
|
// Publish publishes the message.
|
||||||
|
Publish(c context.Context, topic string, message Message) error
|
||||||
|
|
||||||
|
// Subscribe subscribes to the topic. The Receiver function is a callback
|
||||||
|
// function that receives published messages.
|
||||||
|
Subscribe(c context.Context, topic string, receiver Receiver) error
|
||||||
|
|
||||||
|
// Remove removes the named topic.
|
||||||
|
Remove(c context.Context, topic string) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// // global instance of the queue.
|
||||||
|
// var global = New()
|
||||||
|
//
|
||||||
|
// // Set sets the global queue.
|
||||||
|
// func Set(p Publisher) {
|
||||||
|
// global = p
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // Create creates the named topic.
|
||||||
|
// func Create(c context.Context, topic string) error {
|
||||||
|
// return global.Create(c, topic)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // Publish publishes the message.
|
||||||
|
// func Publish(c context.Context, topic string, message Message) error {
|
||||||
|
// return global.Publish(c, topic, message)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // Subscribe subscribes to the topic. The Receiver function is a callback
|
||||||
|
// // function that receives published messages.
|
||||||
|
// func Subscribe(c context.Context, topic string, receiver Receiver) error {
|
||||||
|
// return global.Subscribe(c, topic, receiver)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // Remove removes the topic.
|
||||||
|
// func Remove(c context.Context, topic string) error {
|
||||||
|
// return global.Remove(c, topic)
|
||||||
|
// }
|
45
vendor/github.com/cncd/pubsub/topic.go
generated
vendored
Normal file
45
vendor/github.com/cncd/pubsub/topic.go
generated
vendored
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
package pubsub
|
||||||
|
|
||||||
|
import "sync"
|
||||||
|
|
||||||
|
type topic struct {
|
||||||
|
sync.Mutex
|
||||||
|
|
||||||
|
name string
|
||||||
|
done chan bool
|
||||||
|
subs map[*subscriber]struct{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newTopic(dest string) *topic {
|
||||||
|
return &topic{
|
||||||
|
name: dest,
|
||||||
|
done: make(chan bool),
|
||||||
|
subs: make(map[*subscriber]struct{}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *topic) subscribe(s *subscriber) {
|
||||||
|
t.Lock()
|
||||||
|
t.subs[s] = struct{}{}
|
||||||
|
t.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *topic) unsubscribe(s *subscriber) {
|
||||||
|
t.Lock()
|
||||||
|
delete(t.subs, s)
|
||||||
|
t.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *topic) publish(m Message) {
|
||||||
|
t.Lock()
|
||||||
|
for s := range t.subs {
|
||||||
|
go s.receiver(m)
|
||||||
|
}
|
||||||
|
t.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *topic) close() {
|
||||||
|
t.Lock()
|
||||||
|
close(t.done)
|
||||||
|
t.Unlock()
|
||||||
|
}
|
29
vendor/github.com/cncd/queue/LICENSE
generated
vendored
Normal file
29
vendor/github.com/cncd/queue/LICENSE
generated
vendored
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
BSD 3-Clause License
|
||||||
|
|
||||||
|
Copyright (c) 2017, Brad Rydzewski
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright notice, this
|
||||||
|
list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
* Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation
|
||||||
|
and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
* Neither the name of the copyright holder nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived from
|
||||||
|
this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
6
vendor/github.com/cncd/queue/README
generated
vendored
Normal file
6
vendor/github.com/cncd/queue/README
generated
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
Go package provides a common interface for working with task queues.
|
||||||
|
|
||||||
|
Documentation:
|
||||||
|
|
||||||
|
http://godoc.org/github.com/cncd/queue
|
||||||
|
http://godoc.org/github.com/cncd/queue/gcp
|
190
vendor/github.com/cncd/queue/fifo.go
generated
vendored
Normal file
190
vendor/github.com/cncd/queue/fifo.go
generated
vendored
Normal file
@ -0,0 +1,190 @@
|
|||||||
|
package queue
|
||||||
|
|
||||||
|
import (
|
||||||
|
"container/list"
|
||||||
|
"context"
|
||||||
|
"log"
|
||||||
|
"runtime"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type entry struct {
|
||||||
|
item *Task
|
||||||
|
done chan bool
|
||||||
|
retry int
|
||||||
|
error error
|
||||||
|
deadline time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
type worker struct {
|
||||||
|
filter Filter
|
||||||
|
channel chan *Task
|
||||||
|
}
|
||||||
|
|
||||||
|
type fifo struct {
|
||||||
|
sync.Mutex
|
||||||
|
|
||||||
|
workers map[*worker]struct{}
|
||||||
|
running map[string]*entry
|
||||||
|
pending *list.List
|
||||||
|
extension time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
// New returns a new fifo queue.
|
||||||
|
func New() Queue {
|
||||||
|
return &fifo{
|
||||||
|
workers: map[*worker]struct{}{},
|
||||||
|
running: map[string]*entry{},
|
||||||
|
pending: list.New(),
|
||||||
|
extension: time.Minute * 10,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Push pushes an item to the tail of this queue.
|
||||||
|
func (q *fifo) Push(c context.Context, task *Task) error {
|
||||||
|
q.Lock()
|
||||||
|
q.pending.PushBack(task)
|
||||||
|
q.Unlock()
|
||||||
|
go q.process()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Poll retrieves and removes the head of this queue.
|
||||||
|
func (q *fifo) Poll(c context.Context, f Filter) (*Task, error) {
|
||||||
|
q.Lock()
|
||||||
|
w := &worker{
|
||||||
|
channel: make(chan *Task, 1),
|
||||||
|
filter: f,
|
||||||
|
}
|
||||||
|
q.workers[w] = struct{}{}
|
||||||
|
q.Unlock()
|
||||||
|
go q.process()
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-c.Done():
|
||||||
|
q.Lock()
|
||||||
|
delete(q.workers, w)
|
||||||
|
q.Unlock()
|
||||||
|
return nil, nil
|
||||||
|
case t := <-w.channel:
|
||||||
|
return t, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Done signals that the item is done executing.
|
||||||
|
func (q *fifo) Done(c context.Context, id string) error {
|
||||||
|
return q.Error(c, id, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error signals that the item is done executing with error.
|
||||||
|
func (q *fifo) Error(c context.Context, id string, err error) error {
|
||||||
|
q.Lock()
|
||||||
|
state, ok := q.running[id]
|
||||||
|
if ok {
|
||||||
|
state.error = err
|
||||||
|
close(state.done)
|
||||||
|
delete(q.running, id)
|
||||||
|
}
|
||||||
|
q.Unlock()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait waits until the item is done executing.
|
||||||
|
func (q *fifo) Wait(c context.Context, id string) error {
|
||||||
|
q.Lock()
|
||||||
|
state := q.running[id]
|
||||||
|
q.Unlock()
|
||||||
|
if state != nil {
|
||||||
|
select {
|
||||||
|
case <-c.Done():
|
||||||
|
case <-state.done:
|
||||||
|
return state.error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extend extends the task execution deadline.
|
||||||
|
func (q *fifo) Extend(c context.Context, id string) error {
|
||||||
|
q.Lock()
|
||||||
|
defer q.Unlock()
|
||||||
|
|
||||||
|
state, ok := q.running[id]
|
||||||
|
if ok {
|
||||||
|
state.deadline = time.Now().Add(q.extension)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return ErrNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
// Info returns internal queue information.
|
||||||
|
func (q *fifo) Info(c context.Context) InfoT {
|
||||||
|
q.Lock()
|
||||||
|
stats := InfoT{}
|
||||||
|
stats.Stats.Workers = len(q.workers)
|
||||||
|
stats.Stats.Pending = q.pending.Len()
|
||||||
|
stats.Stats.Running = len(q.running)
|
||||||
|
|
||||||
|
for e := q.pending.Front(); e != nil; e = e.Next() {
|
||||||
|
stats.Pending = append(stats.Pending, e.Value.(*Task))
|
||||||
|
}
|
||||||
|
for _, entry := range q.running {
|
||||||
|
stats.Running = append(stats.Running, entry.item)
|
||||||
|
}
|
||||||
|
|
||||||
|
q.Unlock()
|
||||||
|
return stats
|
||||||
|
}
|
||||||
|
|
||||||
|
// helper function that loops through the queue and attempts to
|
||||||
|
// match the item to a single subscriber.
|
||||||
|
func (q *fifo) process() {
|
||||||
|
defer func() {
|
||||||
|
// the risk of panic is low. This code can probably be removed
|
||||||
|
// once the code has been used in real world installs without issue.
|
||||||
|
if err := recover(); err != nil {
|
||||||
|
const size = 64 << 10
|
||||||
|
buf := make([]byte, size)
|
||||||
|
buf = buf[:runtime.Stack(buf, false)]
|
||||||
|
log.Printf("queue: unexpected panic: %v\n%s", err, buf)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
q.Lock()
|
||||||
|
defer q.Unlock()
|
||||||
|
|
||||||
|
// TODO(bradrydzewski) move this to a helper function
|
||||||
|
// push items to the front of the queue if the item expires.
|
||||||
|
for id, state := range q.running {
|
||||||
|
if time.Now().After(state.deadline) {
|
||||||
|
q.pending.PushFront(state.item)
|
||||||
|
delete(q.running, id)
|
||||||
|
close(state.done)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var next *list.Element
|
||||||
|
loop:
|
||||||
|
for e := q.pending.Front(); e != nil; e = next {
|
||||||
|
next = e.Next()
|
||||||
|
item := e.Value.(*Task)
|
||||||
|
for w := range q.workers {
|
||||||
|
if w.filter(item) {
|
||||||
|
delete(q.workers, w)
|
||||||
|
q.pending.Remove(e)
|
||||||
|
|
||||||
|
q.running[item.ID] = &entry{
|
||||||
|
item: item,
|
||||||
|
done: make(chan bool),
|
||||||
|
deadline: time.Now().Add(q.extension),
|
||||||
|
}
|
||||||
|
|
||||||
|
w.channel <- item
|
||||||
|
break loop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
110
vendor/github.com/cncd/queue/queue.go
generated
vendored
Normal file
110
vendor/github.com/cncd/queue/queue.go
generated
vendored
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
package queue
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ErrCancel indicates the task was cancelled.
|
||||||
|
ErrCancel = errors.New("queue: task cancelled")
|
||||||
|
|
||||||
|
// ErrNotFound indicates the task was not found in the queue.
|
||||||
|
ErrNotFound = errors.New("queue: task not found")
|
||||||
|
)
|
||||||
|
|
||||||
|
// Task defines a unit of work in the queue.
|
||||||
|
type Task struct {
|
||||||
|
// ID identifies this task.
|
||||||
|
ID string `json:"id,omitempty"`
|
||||||
|
|
||||||
|
// Data is the actual data in the entry.
|
||||||
|
Data []byte `json:"data"`
|
||||||
|
|
||||||
|
// Labels represents the key-value pairs the entry is lebeled with.
|
||||||
|
Labels map[string]string `json:"labels,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// InfoT provides runtime information.
|
||||||
|
type InfoT struct {
|
||||||
|
Pending []*Task `json:"pending"`
|
||||||
|
Running []*Task `json:"running"`
|
||||||
|
Stats struct {
|
||||||
|
Workers int `json:"worker_count"`
|
||||||
|
Pending int `json:"pending_count"`
|
||||||
|
Running int `json:"running_count"`
|
||||||
|
Complete int `json:"completed_count"`
|
||||||
|
} `json:"stats"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter filters tasks in the queue. If the Filter returns false,
|
||||||
|
// the Task is skipped and not returned to the subscriber.
|
||||||
|
type Filter func(*Task) bool
|
||||||
|
|
||||||
|
// Queue defines a task queue for scheduling tasks among
|
||||||
|
// a pool of workers.
|
||||||
|
type Queue interface {
|
||||||
|
// Push pushes an task to the tail of this queue.
|
||||||
|
Push(c context.Context, task *Task) error
|
||||||
|
|
||||||
|
// Poll retrieves and removes a task head of this queue.
|
||||||
|
Poll(c context.Context, f Filter) (*Task, error)
|
||||||
|
|
||||||
|
// Extend extends the deadline for a task.
|
||||||
|
Extend(c context.Context, id string) error
|
||||||
|
|
||||||
|
// Done signals the task is complete.
|
||||||
|
Done(c context.Context, id string) error
|
||||||
|
|
||||||
|
// Error signals the task is complete with errors.
|
||||||
|
Error(c context.Context, id string, err error) error
|
||||||
|
|
||||||
|
// Wait waits until the task is complete.
|
||||||
|
Wait(c context.Context, id string) error
|
||||||
|
|
||||||
|
// Info returns internal queue information.
|
||||||
|
Info(c context.Context) InfoT
|
||||||
|
}
|
||||||
|
|
||||||
|
// // global instance of the queue.
|
||||||
|
// var global = New()
|
||||||
|
//
|
||||||
|
// // Set sets the global queue.
|
||||||
|
// func Set(queue Queue) {
|
||||||
|
// global = queue
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // Push pushes an task to the tail of the global queue.
|
||||||
|
// func Push(c context.Context, task *Task) error {
|
||||||
|
// return global.Push(c, task)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // Poll retrieves and removes a task head of the global queue.
|
||||||
|
// func Poll(c context.Context, f Filter) (*Task, error) {
|
||||||
|
// return global.Poll(c, f)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // Extend extends the deadline for a task.
|
||||||
|
// func Extend(c context.Context, id string) error {
|
||||||
|
// return global.Extend(c, id)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // Done signals the task is complete.
|
||||||
|
// func Done(c context.Context, id string) error {
|
||||||
|
// return global.Done(c, id)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // Error signals the task is complete with errors.
|
||||||
|
// func Error(c context.Context, id string, err error) {
|
||||||
|
// global.Error(c, id, err)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // Wait waits until the task is complete.
|
||||||
|
// func Wait(c context.Context, id string) error {
|
||||||
|
// return global.Wait(c, id)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // Info returns internal queue information.
|
||||||
|
// func Info(c context.Context) InfoT {
|
||||||
|
// return global.Info(c)
|
||||||
|
// }
|
1
vendor/github.com/cncd/queue/worker.go
generated
vendored
Normal file
1
vendor/github.com/cncd/queue/worker.go
generated
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
package queue
|
202
vendor/github.com/docker/distribution/LICENSE
generated
vendored
Normal file
202
vendor/github.com/docker/distribution/LICENSE
generated
vendored
Normal file
@ -0,0 +1,202 @@
|
|||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright {yyyy} {name of copyright owner}
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
12
vendor/github.com/docker/distribution/reference/helpers.go
generated
vendored
Normal file
12
vendor/github.com/docker/distribution/reference/helpers.go
generated
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
package reference
|
||||||
|
|
||||||
|
// IsNameOnly returns true if reference only contains a repo name.
|
||||||
|
func IsNameOnly(ref Named) bool {
|
||||||
|
if _, ok := ref.(NamedTagged); ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if _, ok := ref.(Canonical); ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
22
vendor/github.com/docker/distribution/reference/normalize.go
generated
vendored
Normal file
22
vendor/github.com/docker/distribution/reference/normalize.go
generated
vendored
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
package reference
|
||||||
|
|
||||||
|
var (
|
||||||
|
defaultTag = "latest"
|
||||||
|
)
|
||||||
|
|
||||||
|
// EnsureTagged adds the default tag "latest" to a reference if it only has
|
||||||
|
// a repo name.
|
||||||
|
func EnsureTagged(ref Named) NamedTagged {
|
||||||
|
namedTagged, ok := ref.(NamedTagged)
|
||||||
|
if !ok {
|
||||||
|
namedTagged, err := WithTag(ref, defaultTag)
|
||||||
|
if err != nil {
|
||||||
|
// Default tag must be valid, to create a NamedTagged
|
||||||
|
// type with non-validated input the WithTag function
|
||||||
|
// should be used instead
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return namedTagged
|
||||||
|
}
|
||||||
|
return namedTagged
|
||||||
|
}
|
370
vendor/github.com/docker/distribution/reference/reference.go
generated
vendored
Normal file
370
vendor/github.com/docker/distribution/reference/reference.go
generated
vendored
Normal file
@ -0,0 +1,370 @@
|
|||||||
|
// Package reference provides a general type to represent any way of referencing images within the registry.
|
||||||
|
// Its main purpose is to abstract tags and digests (content-addressable hash).
|
||||||
|
//
|
||||||
|
// Grammar
|
||||||
|
//
|
||||||
|
// reference := name [ ":" tag ] [ "@" digest ]
|
||||||
|
// name := [hostname '/'] component ['/' component]*
|
||||||
|
// hostname := hostcomponent ['.' hostcomponent]* [':' port-number]
|
||||||
|
// hostcomponent := /([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])/
|
||||||
|
// port-number := /[0-9]+/
|
||||||
|
// component := alpha-numeric [separator alpha-numeric]*
|
||||||
|
// alpha-numeric := /[a-z0-9]+/
|
||||||
|
// separator := /[_.]|__|[-]*/
|
||||||
|
//
|
||||||
|
// tag := /[\w][\w.-]{0,127}/
|
||||||
|
//
|
||||||
|
// digest := digest-algorithm ":" digest-hex
|
||||||
|
// digest-algorithm := digest-algorithm-component [ digest-algorithm-separator digest-algorithm-component ]
|
||||||
|
// digest-algorithm-separator := /[+.-_]/
|
||||||
|
// digest-algorithm-component := /[A-Za-z][A-Za-z0-9]*/
|
||||||
|
// digest-hex := /[0-9a-fA-F]{32,}/ ; At least 128 bit digest value
|
||||||
|
package reference
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"path"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/opencontainers/go-digest"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// NameTotalLengthMax is the maximum total number of characters in a repository name.
|
||||||
|
NameTotalLengthMax = 255
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ErrReferenceInvalidFormat represents an error while trying to parse a string as a reference.
|
||||||
|
ErrReferenceInvalidFormat = errors.New("invalid reference format")
|
||||||
|
|
||||||
|
// ErrTagInvalidFormat represents an error while trying to parse a string as a tag.
|
||||||
|
ErrTagInvalidFormat = errors.New("invalid tag format")
|
||||||
|
|
||||||
|
// ErrDigestInvalidFormat represents an error while trying to parse a string as a tag.
|
||||||
|
ErrDigestInvalidFormat = errors.New("invalid digest format")
|
||||||
|
|
||||||
|
// ErrNameContainsUppercase is returned for invalid repository names that contain uppercase characters.
|
||||||
|
ErrNameContainsUppercase = errors.New("repository name must be lowercase")
|
||||||
|
|
||||||
|
// ErrNameEmpty is returned for empty, invalid repository names.
|
||||||
|
ErrNameEmpty = errors.New("repository name must have at least one component")
|
||||||
|
|
||||||
|
// ErrNameTooLong is returned when a repository name is longer than NameTotalLengthMax.
|
||||||
|
ErrNameTooLong = fmt.Errorf("repository name must not be more than %v characters", NameTotalLengthMax)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Reference is an opaque object reference identifier that may include
|
||||||
|
// modifiers such as a hostname, name, tag, and digest.
|
||||||
|
type Reference interface {
|
||||||
|
// String returns the full reference
|
||||||
|
String() string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Field provides a wrapper type for resolving correct reference types when
|
||||||
|
// working with encoding.
|
||||||
|
type Field struct {
|
||||||
|
reference Reference
|
||||||
|
}
|
||||||
|
|
||||||
|
// AsField wraps a reference in a Field for encoding.
|
||||||
|
func AsField(reference Reference) Field {
|
||||||
|
return Field{reference}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reference unwraps the reference type from the field to
|
||||||
|
// return the Reference object. This object should be
|
||||||
|
// of the appropriate type to further check for different
|
||||||
|
// reference types.
|
||||||
|
func (f Field) Reference() Reference {
|
||||||
|
return f.reference
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalText serializes the field to byte text which
|
||||||
|
// is the string of the reference.
|
||||||
|
func (f Field) MarshalText() (p []byte, err error) {
|
||||||
|
return []byte(f.reference.String()), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalText parses text bytes by invoking the
|
||||||
|
// reference parser to ensure the appropriately
|
||||||
|
// typed reference object is wrapped by field.
|
||||||
|
func (f *Field) UnmarshalText(p []byte) error {
|
||||||
|
r, err := Parse(string(p))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
f.reference = r
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Named is an object with a full name
|
||||||
|
type Named interface {
|
||||||
|
Reference
|
||||||
|
Name() string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tagged is an object which has a tag
|
||||||
|
type Tagged interface {
|
||||||
|
Reference
|
||||||
|
Tag() string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NamedTagged is an object including a name and tag.
|
||||||
|
type NamedTagged interface {
|
||||||
|
Named
|
||||||
|
Tag() string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Digested is an object which has a digest
|
||||||
|
// in which it can be referenced by
|
||||||
|
type Digested interface {
|
||||||
|
Reference
|
||||||
|
Digest() digest.Digest
|
||||||
|
}
|
||||||
|
|
||||||
|
// Canonical reference is an object with a fully unique
|
||||||
|
// name including a name with hostname and digest
|
||||||
|
type Canonical interface {
|
||||||
|
Named
|
||||||
|
Digest() digest.Digest
|
||||||
|
}
|
||||||
|
|
||||||
|
// SplitHostname splits a named reference into a
|
||||||
|
// hostname and name string. If no valid hostname is
|
||||||
|
// found, the hostname is empty and the full value
|
||||||
|
// is returned as name
|
||||||
|
func SplitHostname(named Named) (string, string) {
|
||||||
|
name := named.Name()
|
||||||
|
match := anchoredNameRegexp.FindStringSubmatch(name)
|
||||||
|
if len(match) != 3 {
|
||||||
|
return "", name
|
||||||
|
}
|
||||||
|
return match[1], match[2]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse parses s and returns a syntactically valid Reference.
|
||||||
|
// If an error was encountered it is returned, along with a nil Reference.
|
||||||
|
// NOTE: Parse will not handle short digests.
|
||||||
|
func Parse(s string) (Reference, error) {
|
||||||
|
matches := ReferenceRegexp.FindStringSubmatch(s)
|
||||||
|
if matches == nil {
|
||||||
|
if s == "" {
|
||||||
|
return nil, ErrNameEmpty
|
||||||
|
}
|
||||||
|
if ReferenceRegexp.FindStringSubmatch(strings.ToLower(s)) != nil {
|
||||||
|
return nil, ErrNameContainsUppercase
|
||||||
|
}
|
||||||
|
return nil, ErrReferenceInvalidFormat
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(matches[1]) > NameTotalLengthMax {
|
||||||
|
return nil, ErrNameTooLong
|
||||||
|
}
|
||||||
|
|
||||||
|
ref := reference{
|
||||||
|
name: matches[1],
|
||||||
|
tag: matches[2],
|
||||||
|
}
|
||||||
|
if matches[3] != "" {
|
||||||
|
var err error
|
||||||
|
ref.digest, err = digest.Parse(matches[3])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
r := getBestReferenceType(ref)
|
||||||
|
if r == nil {
|
||||||
|
return nil, ErrNameEmpty
|
||||||
|
}
|
||||||
|
|
||||||
|
return r, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseNamed parses s and returns a syntactically valid reference implementing
|
||||||
|
// the Named interface. The reference must have a name, otherwise an error is
|
||||||
|
// returned.
|
||||||
|
// If an error was encountered it is returned, along with a nil Reference.
|
||||||
|
// NOTE: ParseNamed will not handle short digests.
|
||||||
|
func ParseNamed(s string) (Named, error) {
|
||||||
|
ref, err := Parse(s)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
named, isNamed := ref.(Named)
|
||||||
|
if !isNamed {
|
||||||
|
return nil, fmt.Errorf("reference %s has no name", ref.String())
|
||||||
|
}
|
||||||
|
return named, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithName returns a named object representing the given string. If the input
|
||||||
|
// is invalid ErrReferenceInvalidFormat will be returned.
|
||||||
|
func WithName(name string) (Named, error) {
|
||||||
|
if len(name) > NameTotalLengthMax {
|
||||||
|
return nil, ErrNameTooLong
|
||||||
|
}
|
||||||
|
if !anchoredNameRegexp.MatchString(name) {
|
||||||
|
return nil, ErrReferenceInvalidFormat
|
||||||
|
}
|
||||||
|
return repository(name), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithTag combines the name from "name" and the tag from "tag" to form a
|
||||||
|
// reference incorporating both the name and the tag.
|
||||||
|
func WithTag(name Named, tag string) (NamedTagged, error) {
|
||||||
|
if !anchoredTagRegexp.MatchString(tag) {
|
||||||
|
return nil, ErrTagInvalidFormat
|
||||||
|
}
|
||||||
|
if canonical, ok := name.(Canonical); ok {
|
||||||
|
return reference{
|
||||||
|
name: name.Name(),
|
||||||
|
tag: tag,
|
||||||
|
digest: canonical.Digest(),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
return taggedReference{
|
||||||
|
name: name.Name(),
|
||||||
|
tag: tag,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithDigest combines the name from "name" and the digest from "digest" to form
|
||||||
|
// a reference incorporating both the name and the digest.
|
||||||
|
func WithDigest(name Named, digest digest.Digest) (Canonical, error) {
|
||||||
|
if !anchoredDigestRegexp.MatchString(digest.String()) {
|
||||||
|
return nil, ErrDigestInvalidFormat
|
||||||
|
}
|
||||||
|
if tagged, ok := name.(Tagged); ok {
|
||||||
|
return reference{
|
||||||
|
name: name.Name(),
|
||||||
|
tag: tagged.Tag(),
|
||||||
|
digest: digest,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
return canonicalReference{
|
||||||
|
name: name.Name(),
|
||||||
|
digest: digest,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Match reports whether ref matches the specified pattern.
|
||||||
|
// See https://godoc.org/path#Match for supported patterns.
|
||||||
|
func Match(pattern string, ref Reference) (bool, error) {
|
||||||
|
matched, err := path.Match(pattern, ref.String())
|
||||||
|
if namedRef, isNamed := ref.(Named); isNamed && !matched {
|
||||||
|
matched, _ = path.Match(pattern, namedRef.Name())
|
||||||
|
}
|
||||||
|
return matched, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// TrimNamed removes any tag or digest from the named reference.
|
||||||
|
func TrimNamed(ref Named) Named {
|
||||||
|
return repository(ref.Name())
|
||||||
|
}
|
||||||
|
|
||||||
|
func getBestReferenceType(ref reference) Reference {
|
||||||
|
if ref.name == "" {
|
||||||
|
// Allow digest only references
|
||||||
|
if ref.digest != "" {
|
||||||
|
return digestReference(ref.digest)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if ref.tag == "" {
|
||||||
|
if ref.digest != "" {
|
||||||
|
return canonicalReference{
|
||||||
|
name: ref.name,
|
||||||
|
digest: ref.digest,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return repository(ref.name)
|
||||||
|
}
|
||||||
|
if ref.digest == "" {
|
||||||
|
return taggedReference{
|
||||||
|
name: ref.name,
|
||||||
|
tag: ref.tag,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ref
|
||||||
|
}
|
||||||
|
|
||||||
|
type reference struct {
|
||||||
|
name string
|
||||||
|
tag string
|
||||||
|
digest digest.Digest
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r reference) String() string {
|
||||||
|
return r.name + ":" + r.tag + "@" + r.digest.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r reference) Name() string {
|
||||||
|
return r.name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r reference) Tag() string {
|
||||||
|
return r.tag
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r reference) Digest() digest.Digest {
|
||||||
|
return r.digest
|
||||||
|
}
|
||||||
|
|
||||||
|
type repository string
|
||||||
|
|
||||||
|
func (r repository) String() string {
|
||||||
|
return string(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r repository) Name() string {
|
||||||
|
return string(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
type digestReference digest.Digest
|
||||||
|
|
||||||
|
func (d digestReference) String() string {
|
||||||
|
return d.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d digestReference) Digest() digest.Digest {
|
||||||
|
return digest.Digest(d)
|
||||||
|
}
|
||||||
|
|
||||||
|
type taggedReference struct {
|
||||||
|
name string
|
||||||
|
tag string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t taggedReference) String() string {
|
||||||
|
return t.name + ":" + t.tag
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t taggedReference) Name() string {
|
||||||
|
return t.name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t taggedReference) Tag() string {
|
||||||
|
return t.tag
|
||||||
|
}
|
||||||
|
|
||||||
|
type canonicalReference struct {
|
||||||
|
name string
|
||||||
|
digest digest.Digest
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c canonicalReference) String() string {
|
||||||
|
return c.name + "@" + c.digest.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c canonicalReference) Name() string {
|
||||||
|
return c.name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c canonicalReference) Digest() digest.Digest {
|
||||||
|
return c.digest
|
||||||
|
}
|
124
vendor/github.com/docker/distribution/reference/regexp.go
generated
vendored
Normal file
124
vendor/github.com/docker/distribution/reference/regexp.go
generated
vendored
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
package reference
|
||||||
|
|
||||||
|
import "regexp"
|
||||||
|
|
||||||
|
var (
|
||||||
|
// alphaNumericRegexp defines the alpha numeric atom, typically a
|
||||||
|
// component of names. This only allows lower case characters and digits.
|
||||||
|
alphaNumericRegexp = match(`[a-z0-9]+`)
|
||||||
|
|
||||||
|
// separatorRegexp defines the separators allowed to be embedded in name
|
||||||
|
// components. This allow one period, one or two underscore and multiple
|
||||||
|
// dashes.
|
||||||
|
separatorRegexp = match(`(?:[._]|__|[-]*)`)
|
||||||
|
|
||||||
|
// nameComponentRegexp restricts registry path component names to start
|
||||||
|
// with at least one letter or number, with following parts able to be
|
||||||
|
// separated by one period, one or two underscore and multiple dashes.
|
||||||
|
nameComponentRegexp = expression(
|
||||||
|
alphaNumericRegexp,
|
||||||
|
optional(repeated(separatorRegexp, alphaNumericRegexp)))
|
||||||
|
|
||||||
|
// hostnameComponentRegexp restricts the registry hostname component of a
|
||||||
|
// repository name to start with a component as defined by hostnameRegexp
|
||||||
|
// and followed by an optional port.
|
||||||
|
hostnameComponentRegexp = match(`(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])`)
|
||||||
|
|
||||||
|
// hostnameRegexp defines the structure of potential hostname components
|
||||||
|
// that may be part of image names. This is purposely a subset of what is
|
||||||
|
// allowed by DNS to ensure backwards compatibility with Docker image
|
||||||
|
// names.
|
||||||
|
hostnameRegexp = expression(
|
||||||
|
hostnameComponentRegexp,
|
||||||
|
optional(repeated(literal(`.`), hostnameComponentRegexp)),
|
||||||
|
optional(literal(`:`), match(`[0-9]+`)))
|
||||||
|
|
||||||
|
// TagRegexp matches valid tag names. From docker/docker:graph/tags.go.
|
||||||
|
TagRegexp = match(`[\w][\w.-]{0,127}`)
|
||||||
|
|
||||||
|
// anchoredTagRegexp matches valid tag names, anchored at the start and
|
||||||
|
// end of the matched string.
|
||||||
|
anchoredTagRegexp = anchored(TagRegexp)
|
||||||
|
|
||||||
|
// DigestRegexp matches valid digests.
|
||||||
|
DigestRegexp = match(`[A-Za-z][A-Za-z0-9]*(?:[-_+.][A-Za-z][A-Za-z0-9]*)*[:][[:xdigit:]]{32,}`)
|
||||||
|
|
||||||
|
// anchoredDigestRegexp matches valid digests, anchored at the start and
|
||||||
|
// end of the matched string.
|
||||||
|
anchoredDigestRegexp = anchored(DigestRegexp)
|
||||||
|
|
||||||
|
// NameRegexp is the format for the name component of references. The
|
||||||
|
// regexp has capturing groups for the hostname and name part omitting
|
||||||
|
// the separating forward slash from either.
|
||||||
|
NameRegexp = expression(
|
||||||
|
optional(hostnameRegexp, literal(`/`)),
|
||||||
|
nameComponentRegexp,
|
||||||
|
optional(repeated(literal(`/`), nameComponentRegexp)))
|
||||||
|
|
||||||
|
// anchoredNameRegexp is used to parse a name value, capturing the
|
||||||
|
// hostname and trailing components.
|
||||||
|
anchoredNameRegexp = anchored(
|
||||||
|
optional(capture(hostnameRegexp), literal(`/`)),
|
||||||
|
capture(nameComponentRegexp,
|
||||||
|
optional(repeated(literal(`/`), nameComponentRegexp))))
|
||||||
|
|
||||||
|
// ReferenceRegexp is the full supported format of a reference. The regexp
|
||||||
|
// is anchored and has capturing groups for name, tag, and digest
|
||||||
|
// components.
|
||||||
|
ReferenceRegexp = anchored(capture(NameRegexp),
|
||||||
|
optional(literal(":"), capture(TagRegexp)),
|
||||||
|
optional(literal("@"), capture(DigestRegexp)))
|
||||||
|
)
|
||||||
|
|
||||||
|
// match compiles the string to a regular expression.
|
||||||
|
var match = regexp.MustCompile
|
||||||
|
|
||||||
|
// literal compiles s into a literal regular expression, escaping any regexp
|
||||||
|
// reserved characters.
|
||||||
|
func literal(s string) *regexp.Regexp {
|
||||||
|
re := match(regexp.QuoteMeta(s))
|
||||||
|
|
||||||
|
if _, complete := re.LiteralPrefix(); !complete {
|
||||||
|
panic("must be a literal")
|
||||||
|
}
|
||||||
|
|
||||||
|
return re
|
||||||
|
}
|
||||||
|
|
||||||
|
// expression defines a full expression, where each regular expression must
|
||||||
|
// follow the previous.
|
||||||
|
func expression(res ...*regexp.Regexp) *regexp.Regexp {
|
||||||
|
var s string
|
||||||
|
for _, re := range res {
|
||||||
|
s += re.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
return match(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// optional wraps the expression in a non-capturing group and makes the
|
||||||
|
// production optional.
|
||||||
|
func optional(res ...*regexp.Regexp) *regexp.Regexp {
|
||||||
|
return match(group(expression(res...)).String() + `?`)
|
||||||
|
}
|
||||||
|
|
||||||
|
// repeated wraps the regexp in a non-capturing group to get one or more
|
||||||
|
// matches.
|
||||||
|
func repeated(res ...*regexp.Regexp) *regexp.Regexp {
|
||||||
|
return match(group(expression(res...)).String() + `+`)
|
||||||
|
}
|
||||||
|
|
||||||
|
// group wraps the regexp in a non-capturing group.
|
||||||
|
func group(res ...*regexp.Regexp) *regexp.Regexp {
|
||||||
|
return match(`(?:` + expression(res...).String() + `)`)
|
||||||
|
}
|
||||||
|
|
||||||
|
// capture wraps the expression in a capturing group.
|
||||||
|
func capture(res ...*regexp.Regexp) *regexp.Regexp {
|
||||||
|
return match(`(` + expression(res...).String() + `)`)
|
||||||
|
}
|
||||||
|
|
||||||
|
// anchored anchors the regular expression by adding start and end delimiters.
|
||||||
|
func anchored(res ...*regexp.Regexp) *regexp.Regexp {
|
||||||
|
return match(`^` + expression(res...).String() + `$`)
|
||||||
|
}
|
2
vendor/github.com/docker/docker/LICENSE
generated
vendored
2
vendor/github.com/docker/docker/LICENSE
generated
vendored
@ -176,7 +176,7 @@
|
|||||||
|
|
||||||
END OF TERMS AND CONDITIONS
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
Copyright 2013-2015 Docker, Inc.
|
Copyright 2013-2016 Docker, Inc.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
2
vendor/github.com/docker/docker/NOTICE
generated
vendored
2
vendor/github.com/docker/docker/NOTICE
generated
vendored
@ -1,5 +1,5 @@
|
|||||||
Docker
|
Docker
|
||||||
Copyright 2012-2015 Docker, Inc.
|
Copyright 2012-2016 Docker, Inc.
|
||||||
|
|
||||||
This product includes software developed at Docker, Inc. (https://www.docker.com).
|
This product includes software developed at Docker, Inc. (https://www.docker.com).
|
||||||
|
|
||||||
|
22
vendor/github.com/docker/docker/api/types/auth.go
generated
vendored
Normal file
22
vendor/github.com/docker/docker/api/types/auth.go
generated
vendored
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
package types
|
||||||
|
|
||||||
|
// AuthConfig contains authorization information for connecting to a Registry
|
||||||
|
type AuthConfig struct {
|
||||||
|
Username string `json:"username,omitempty"`
|
||||||
|
Password string `json:"password,omitempty"`
|
||||||
|
Auth string `json:"auth,omitempty"`
|
||||||
|
|
||||||
|
// Email is an optional value associated with the username.
|
||||||
|
// This field is deprecated and will be removed in a later
|
||||||
|
// version of docker.
|
||||||
|
Email string `json:"email,omitempty"`
|
||||||
|
|
||||||
|
ServerAddress string `json:"serveraddress,omitempty"`
|
||||||
|
|
||||||
|
// IdentityToken is used to authenticate the user and get
|
||||||
|
// an access token for the registry.
|
||||||
|
IdentityToken string `json:"identitytoken,omitempty"`
|
||||||
|
|
||||||
|
// RegistryToken is a bearer token to be sent to a registry
|
||||||
|
RegistryToken string `json:"registrytoken,omitempty"`
|
||||||
|
}
|
23
vendor/github.com/docker/docker/api/types/blkiodev/blkio.go
generated
vendored
Normal file
23
vendor/github.com/docker/docker/api/types/blkiodev/blkio.go
generated
vendored
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
package blkiodev
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
// WeightDevice is a structure that holds device:weight pair
|
||||||
|
type WeightDevice struct {
|
||||||
|
Path string
|
||||||
|
Weight uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *WeightDevice) String() string {
|
||||||
|
return fmt.Sprintf("%s:%d", w.Path, w.Weight)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ThrottleDevice is a structure that holds device:rate_per_second pair
|
||||||
|
type ThrottleDevice struct {
|
||||||
|
Path string
|
||||||
|
Rate uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *ThrottleDevice) String() string {
|
||||||
|
return fmt.Sprintf("%s:%d", t.Path, t.Rate)
|
||||||
|
}
|
378
vendor/github.com/docker/docker/api/types/client.go
generated
vendored
Normal file
378
vendor/github.com/docker/docker/api/types/client.go
generated
vendored
Normal file
@ -0,0 +1,378 @@
|
|||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/docker/docker/api/types/container"
|
||||||
|
"github.com/docker/docker/api/types/filters"
|
||||||
|
"github.com/docker/go-units"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CheckpointCreateOptions holds parameters to create a checkpoint from a container
|
||||||
|
type CheckpointCreateOptions struct {
|
||||||
|
CheckpointID string
|
||||||
|
CheckpointDir string
|
||||||
|
Exit bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckpointListOptions holds parameters to list checkpoints for a container
|
||||||
|
type CheckpointListOptions struct {
|
||||||
|
CheckpointDir string
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckpointDeleteOptions holds parameters to delete a checkpoint from a container
|
||||||
|
type CheckpointDeleteOptions struct {
|
||||||
|
CheckpointID string
|
||||||
|
CheckpointDir string
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContainerAttachOptions holds parameters to attach to a container.
|
||||||
|
type ContainerAttachOptions struct {
|
||||||
|
Stream bool
|
||||||
|
Stdin bool
|
||||||
|
Stdout bool
|
||||||
|
Stderr bool
|
||||||
|
DetachKeys string
|
||||||
|
Logs bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContainerCommitOptions holds parameters to commit changes into a container.
|
||||||
|
type ContainerCommitOptions struct {
|
||||||
|
Reference string
|
||||||
|
Comment string
|
||||||
|
Author string
|
||||||
|
Changes []string
|
||||||
|
Pause bool
|
||||||
|
Config *container.Config
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContainerExecInspect holds information returned by exec inspect.
|
||||||
|
type ContainerExecInspect struct {
|
||||||
|
ExecID string
|
||||||
|
ContainerID string
|
||||||
|
Running bool
|
||||||
|
ExitCode int
|
||||||
|
Pid int
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContainerListOptions holds parameters to list containers with.
|
||||||
|
type ContainerListOptions struct {
|
||||||
|
Quiet bool
|
||||||
|
Size bool
|
||||||
|
All bool
|
||||||
|
Latest bool
|
||||||
|
Since string
|
||||||
|
Before string
|
||||||
|
Limit int
|
||||||
|
Filters filters.Args
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContainerLogsOptions holds parameters to filter logs with.
|
||||||
|
type ContainerLogsOptions struct {
|
||||||
|
ShowStdout bool
|
||||||
|
ShowStderr bool
|
||||||
|
Since string
|
||||||
|
Timestamps bool
|
||||||
|
Follow bool
|
||||||
|
Tail string
|
||||||
|
Details bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContainerRemoveOptions holds parameters to remove containers.
|
||||||
|
type ContainerRemoveOptions struct {
|
||||||
|
RemoveVolumes bool
|
||||||
|
RemoveLinks bool
|
||||||
|
Force bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContainerStartOptions holds parameters to start containers.
|
||||||
|
type ContainerStartOptions struct {
|
||||||
|
CheckpointID string
|
||||||
|
CheckpointDir string
|
||||||
|
}
|
||||||
|
|
||||||
|
// CopyToContainerOptions holds information
|
||||||
|
// about files to copy into a container
|
||||||
|
type CopyToContainerOptions struct {
|
||||||
|
AllowOverwriteDirWithFile bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// EventsOptions holds parameters to filter events with.
|
||||||
|
type EventsOptions struct {
|
||||||
|
Since string
|
||||||
|
Until string
|
||||||
|
Filters filters.Args
|
||||||
|
}
|
||||||
|
|
||||||
|
// NetworkListOptions holds parameters to filter the list of networks with.
|
||||||
|
type NetworkListOptions struct {
|
||||||
|
Filters filters.Args
|
||||||
|
}
|
||||||
|
|
||||||
|
// HijackedResponse holds connection information for a hijacked request.
|
||||||
|
type HijackedResponse struct {
|
||||||
|
Conn net.Conn
|
||||||
|
Reader *bufio.Reader
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close closes the hijacked connection and reader.
|
||||||
|
func (h *HijackedResponse) Close() {
|
||||||
|
h.Conn.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// CloseWriter is an interface that implements structs
|
||||||
|
// that close input streams to prevent from writing.
|
||||||
|
type CloseWriter interface {
|
||||||
|
CloseWrite() error
|
||||||
|
}
|
||||||
|
|
||||||
|
// CloseWrite closes a readWriter for writing.
|
||||||
|
func (h *HijackedResponse) CloseWrite() error {
|
||||||
|
if conn, ok := h.Conn.(CloseWriter); ok {
|
||||||
|
return conn.CloseWrite()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ImageBuildOptions holds the information
|
||||||
|
// necessary to build images.
|
||||||
|
type ImageBuildOptions struct {
|
||||||
|
Tags []string
|
||||||
|
SuppressOutput bool
|
||||||
|
RemoteContext string
|
||||||
|
NoCache bool
|
||||||
|
Remove bool
|
||||||
|
ForceRemove bool
|
||||||
|
PullParent bool
|
||||||
|
Isolation container.Isolation
|
||||||
|
CPUSetCPUs string
|
||||||
|
CPUSetMems string
|
||||||
|
CPUShares int64
|
||||||
|
CPUQuota int64
|
||||||
|
CPUPeriod int64
|
||||||
|
Memory int64
|
||||||
|
MemorySwap int64
|
||||||
|
CgroupParent string
|
||||||
|
NetworkMode string
|
||||||
|
ShmSize int64
|
||||||
|
Dockerfile string
|
||||||
|
Ulimits []*units.Ulimit
|
||||||
|
// See the parsing of buildArgs in api/server/router/build/build_routes.go
|
||||||
|
// for an explanation of why BuildArgs needs to use *string instead of
|
||||||
|
// just a string
|
||||||
|
BuildArgs map[string]*string
|
||||||
|
AuthConfigs map[string]AuthConfig
|
||||||
|
Context io.Reader
|
||||||
|
Labels map[string]string
|
||||||
|
// squash the resulting image's layers to the parent
|
||||||
|
// preserves the original image and creates a new one from the parent with all
|
||||||
|
// the changes applied to a single layer
|
||||||
|
Squash bool
|
||||||
|
// CacheFrom specifies images that are used for matching cache. Images
|
||||||
|
// specified here do not need to have a valid parent chain to match cache.
|
||||||
|
CacheFrom []string
|
||||||
|
SecurityOpt []string
|
||||||
|
}
|
||||||
|
|
||||||
|
// ImageBuildResponse holds information
|
||||||
|
// returned by a server after building
|
||||||
|
// an image.
|
||||||
|
type ImageBuildResponse struct {
|
||||||
|
Body io.ReadCloser
|
||||||
|
OSType string
|
||||||
|
}
|
||||||
|
|
||||||
|
// ImageCreateOptions holds information to create images.
|
||||||
|
type ImageCreateOptions struct {
|
||||||
|
RegistryAuth string // RegistryAuth is the base64 encoded credentials for the registry
|
||||||
|
}
|
||||||
|
|
||||||
|
// ImageImportSource holds source information for ImageImport
|
||||||
|
type ImageImportSource struct {
|
||||||
|
Source io.Reader // Source is the data to send to the server to create this image from. You must set SourceName to "-" to leverage this.
|
||||||
|
SourceName string // SourceName is the name of the image to pull. Set to "-" to leverage the Source attribute.
|
||||||
|
}
|
||||||
|
|
||||||
|
// ImageImportOptions holds information to import images from the client host.
|
||||||
|
type ImageImportOptions struct {
|
||||||
|
Tag string // Tag is the name to tag this image with. This attribute is deprecated.
|
||||||
|
Message string // Message is the message to tag the image with
|
||||||
|
Changes []string // Changes are the raw changes to apply to this image
|
||||||
|
}
|
||||||
|
|
||||||
|
// ImageListOptions holds parameters to filter the list of images with.
|
||||||
|
type ImageListOptions struct {
|
||||||
|
All bool
|
||||||
|
Filters filters.Args
|
||||||
|
}
|
||||||
|
|
||||||
|
// ImageLoadResponse returns information to the client about a load process.
|
||||||
|
type ImageLoadResponse struct {
|
||||||
|
// Body must be closed to avoid a resource leak
|
||||||
|
Body io.ReadCloser
|
||||||
|
JSON bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// ImagePullOptions holds information to pull images.
|
||||||
|
type ImagePullOptions struct {
|
||||||
|
All bool
|
||||||
|
RegistryAuth string // RegistryAuth is the base64 encoded credentials for the registry
|
||||||
|
PrivilegeFunc RequestPrivilegeFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
// RequestPrivilegeFunc is a function interface that
|
||||||
|
// clients can supply to retry operations after
|
||||||
|
// getting an authorization error.
|
||||||
|
// This function returns the registry authentication
|
||||||
|
// header value in base 64 format, or an error
|
||||||
|
// if the privilege request fails.
|
||||||
|
type RequestPrivilegeFunc func() (string, error)
|
||||||
|
|
||||||
|
//ImagePushOptions holds information to push images.
|
||||||
|
type ImagePushOptions ImagePullOptions
|
||||||
|
|
||||||
|
// ImageRemoveOptions holds parameters to remove images.
|
||||||
|
type ImageRemoveOptions struct {
|
||||||
|
Force bool
|
||||||
|
PruneChildren bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// ImageSearchOptions holds parameters to search images with.
|
||||||
|
type ImageSearchOptions struct {
|
||||||
|
RegistryAuth string
|
||||||
|
PrivilegeFunc RequestPrivilegeFunc
|
||||||
|
Filters filters.Args
|
||||||
|
Limit int
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResizeOptions holds parameters to resize a tty.
|
||||||
|
// It can be used to resize container ttys and
|
||||||
|
// exec process ttys too.
|
||||||
|
type ResizeOptions struct {
|
||||||
|
Height uint
|
||||||
|
Width uint
|
||||||
|
}
|
||||||
|
|
||||||
|
// VersionResponse holds version information for the client and the server
|
||||||
|
type VersionResponse struct {
|
||||||
|
Client *Version
|
||||||
|
Server *Version
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServerOK returns true when the client could connect to the docker server
|
||||||
|
// and parse the information received. It returns false otherwise.
|
||||||
|
func (v VersionResponse) ServerOK() bool {
|
||||||
|
return v.Server != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NodeListOptions holds parameters to list nodes with.
|
||||||
|
type NodeListOptions struct {
|
||||||
|
Filters filters.Args
|
||||||
|
}
|
||||||
|
|
||||||
|
// NodeRemoveOptions holds parameters to remove nodes with.
|
||||||
|
type NodeRemoveOptions struct {
|
||||||
|
Force bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServiceCreateOptions contains the options to use when creating a service.
|
||||||
|
type ServiceCreateOptions struct {
|
||||||
|
// EncodedRegistryAuth is the encoded registry authorization credentials to
|
||||||
|
// use when updating the service.
|
||||||
|
//
|
||||||
|
// This field follows the format of the X-Registry-Auth header.
|
||||||
|
EncodedRegistryAuth string
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServiceCreateResponse contains the information returned to a client
|
||||||
|
// on the creation of a new service.
|
||||||
|
type ServiceCreateResponse struct {
|
||||||
|
// ID is the ID of the created service.
|
||||||
|
ID string
|
||||||
|
// Warnings is a set of non-fatal warning messages to pass on to the user.
|
||||||
|
Warnings []string `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Values for RegistryAuthFrom in ServiceUpdateOptions
|
||||||
|
const (
|
||||||
|
RegistryAuthFromSpec = "spec"
|
||||||
|
RegistryAuthFromPreviousSpec = "previous-spec"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ServiceUpdateOptions contains the options to be used for updating services.
|
||||||
|
type ServiceUpdateOptions struct {
|
||||||
|
// EncodedRegistryAuth is the encoded registry authorization credentials to
|
||||||
|
// use when updating the service.
|
||||||
|
//
|
||||||
|
// This field follows the format of the X-Registry-Auth header.
|
||||||
|
EncodedRegistryAuth string
|
||||||
|
|
||||||
|
// TODO(stevvooe): Consider moving the version parameter of ServiceUpdate
|
||||||
|
// into this field. While it does open API users up to racy writes, most
|
||||||
|
// users may not need that level of consistency in practice.
|
||||||
|
|
||||||
|
// RegistryAuthFrom specifies where to find the registry authorization
|
||||||
|
// credentials if they are not given in EncodedRegistryAuth. Valid
|
||||||
|
// values are "spec" and "previous-spec".
|
||||||
|
RegistryAuthFrom string
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServiceListOptions holds parameters to list services with.
|
||||||
|
type ServiceListOptions struct {
|
||||||
|
Filters filters.Args
|
||||||
|
}
|
||||||
|
|
||||||
|
// TaskListOptions holds parameters to list tasks with.
|
||||||
|
type TaskListOptions struct {
|
||||||
|
Filters filters.Args
|
||||||
|
}
|
||||||
|
|
||||||
|
// PluginRemoveOptions holds parameters to remove plugins.
|
||||||
|
type PluginRemoveOptions struct {
|
||||||
|
Force bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// PluginEnableOptions holds parameters to enable plugins.
|
||||||
|
type PluginEnableOptions struct {
|
||||||
|
Timeout int
|
||||||
|
}
|
||||||
|
|
||||||
|
// PluginDisableOptions holds parameters to disable plugins.
|
||||||
|
type PluginDisableOptions struct {
|
||||||
|
Force bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// PluginInstallOptions holds parameters to install a plugin.
|
||||||
|
type PluginInstallOptions struct {
|
||||||
|
Disabled bool
|
||||||
|
AcceptAllPermissions bool
|
||||||
|
RegistryAuth string // RegistryAuth is the base64 encoded credentials for the registry
|
||||||
|
RemoteRef string // RemoteRef is the plugin name on the registry
|
||||||
|
PrivilegeFunc RequestPrivilegeFunc
|
||||||
|
AcceptPermissionsFunc func(PluginPrivileges) (bool, error)
|
||||||
|
Args []string
|
||||||
|
}
|
||||||
|
|
||||||
|
// SecretRequestOption is a type for requesting secrets
|
||||||
|
type SecretRequestOption struct {
|
||||||
|
Source string
|
||||||
|
Target string
|
||||||
|
UID string
|
||||||
|
GID string
|
||||||
|
Mode os.FileMode
|
||||||
|
}
|
||||||
|
|
||||||
|
// SwarmUnlockKeyResponse contains the response for Engine API:
|
||||||
|
// GET /swarm/unlockkey
|
||||||
|
type SwarmUnlockKeyResponse struct {
|
||||||
|
// UnlockKey is the unlock key in ASCII-armored format.
|
||||||
|
UnlockKey string
|
||||||
|
}
|
||||||
|
|
||||||
|
// PluginCreateOptions hold all options to plugin create.
|
||||||
|
type PluginCreateOptions struct {
|
||||||
|
RepoName string
|
||||||
|
}
|
69
vendor/github.com/docker/docker/api/types/configs.go
generated
vendored
Normal file
69
vendor/github.com/docker/docker/api/types/configs.go
generated
vendored
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/docker/docker/api/types/container"
|
||||||
|
"github.com/docker/docker/api/types/network"
|
||||||
|
)
|
||||||
|
|
||||||
|
// configs holds structs used for internal communication between the
|
||||||
|
// frontend (such as an http server) and the backend (such as the
|
||||||
|
// docker daemon).
|
||||||
|
|
||||||
|
// ContainerCreateConfig is the parameter set to ContainerCreate()
|
||||||
|
type ContainerCreateConfig struct {
|
||||||
|
Name string
|
||||||
|
Config *container.Config
|
||||||
|
HostConfig *container.HostConfig
|
||||||
|
NetworkingConfig *network.NetworkingConfig
|
||||||
|
AdjustCPUShares bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContainerRmConfig holds arguments for the container remove
|
||||||
|
// operation. This struct is used to tell the backend what operations
|
||||||
|
// to perform.
|
||||||
|
type ContainerRmConfig struct {
|
||||||
|
ForceRemove, RemoveVolume, RemoveLink bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContainerCommitConfig contains build configs for commit operation,
|
||||||
|
// and is used when making a commit with the current state of the container.
|
||||||
|
type ContainerCommitConfig struct {
|
||||||
|
Pause bool
|
||||||
|
Repo string
|
||||||
|
Tag string
|
||||||
|
Author string
|
||||||
|
Comment string
|
||||||
|
// merge container config into commit config before commit
|
||||||
|
MergeConfigs bool
|
||||||
|
Config *container.Config
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExecConfig is a small subset of the Config struct that holds the configuration
|
||||||
|
// for the exec feature of docker.
|
||||||
|
type ExecConfig struct {
|
||||||
|
User string // User that will run the command
|
||||||
|
Privileged bool // Is the container in privileged mode
|
||||||
|
Tty bool // Attach standard streams to a tty.
|
||||||
|
AttachStdin bool // Attach the standard input, makes possible user interaction
|
||||||
|
AttachStderr bool // Attach the standard error
|
||||||
|
AttachStdout bool // Attach the standard output
|
||||||
|
Detach bool // Execute in detach mode
|
||||||
|
DetachKeys string // Escape keys for detach
|
||||||
|
Env []string // Environment variables
|
||||||
|
Cmd []string // Execution commands and args
|
||||||
|
}
|
||||||
|
|
||||||
|
// PluginRmConfig holds arguments for plugin remove.
|
||||||
|
type PluginRmConfig struct {
|
||||||
|
ForceRemove bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// PluginEnableConfig holds arguments for plugin enable
|
||||||
|
type PluginEnableConfig struct {
|
||||||
|
Timeout int
|
||||||
|
}
|
||||||
|
|
||||||
|
// PluginDisableConfig holds arguments for plugin disable.
|
||||||
|
type PluginDisableConfig struct {
|
||||||
|
ForceDisable bool
|
||||||
|
}
|
62
vendor/github.com/docker/docker/api/types/container/config.go
generated
vendored
Normal file
62
vendor/github.com/docker/docker/api/types/container/config.go
generated
vendored
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
package container
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/docker/docker/api/types/strslice"
|
||||||
|
"github.com/docker/go-connections/nat"
|
||||||
|
)
|
||||||
|
|
||||||
|
// HealthConfig holds configuration settings for the HEALTHCHECK feature.
|
||||||
|
type HealthConfig struct {
|
||||||
|
// Test is the test to perform to check that the container is healthy.
|
||||||
|
// An empty slice means to inherit the default.
|
||||||
|
// The options are:
|
||||||
|
// {} : inherit healthcheck
|
||||||
|
// {"NONE"} : disable healthcheck
|
||||||
|
// {"CMD", args...} : exec arguments directly
|
||||||
|
// {"CMD-SHELL", command} : run command with system's default shell
|
||||||
|
Test []string `json:",omitempty"`
|
||||||
|
|
||||||
|
// Zero means to inherit. Durations are expressed as integer nanoseconds.
|
||||||
|
Interval time.Duration `json:",omitempty"` // Interval is the time to wait between checks.
|
||||||
|
Timeout time.Duration `json:",omitempty"` // Timeout is the time to wait before considering the check to have hung.
|
||||||
|
|
||||||
|
// Retries is the number of consecutive failures needed to consider a container as unhealthy.
|
||||||
|
// Zero means inherit.
|
||||||
|
Retries int `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Config contains the configuration data about a container.
|
||||||
|
// It should hold only portable information about the container.
|
||||||
|
// Here, "portable" means "independent from the host we are running on".
|
||||||
|
// Non-portable information *should* appear in HostConfig.
|
||||||
|
// All fields added to this struct must be marked `omitempty` to keep getting
|
||||||
|
// predictable hashes from the old `v1Compatibility` configuration.
|
||||||
|
type Config struct {
|
||||||
|
Hostname string // Hostname
|
||||||
|
Domainname string // Domainname
|
||||||
|
User string // User that will run the command(s) inside the container, also support user:group
|
||||||
|
AttachStdin bool // Attach the standard input, makes possible user interaction
|
||||||
|
AttachStdout bool // Attach the standard output
|
||||||
|
AttachStderr bool // Attach the standard error
|
||||||
|
ExposedPorts nat.PortSet `json:",omitempty"` // List of exposed ports
|
||||||
|
Tty bool // Attach standard streams to a tty, including stdin if it is not closed.
|
||||||
|
OpenStdin bool // Open stdin
|
||||||
|
StdinOnce bool // If true, close stdin after the 1 attached client disconnects.
|
||||||
|
Env []string // List of environment variable to set in the container
|
||||||
|
Cmd strslice.StrSlice // Command to run when starting the container
|
||||||
|
Healthcheck *HealthConfig `json:",omitempty"` // Healthcheck describes how to check the container is healthy
|
||||||
|
ArgsEscaped bool `json:",omitempty"` // True if command is already escaped (Windows specific)
|
||||||
|
Image string // Name of the image as it was passed by the operator (e.g. could be symbolic)
|
||||||
|
Volumes map[string]struct{} // List of volumes (mounts) used for the container
|
||||||
|
WorkingDir string // Current directory (PWD) in the command will be launched
|
||||||
|
Entrypoint strslice.StrSlice // Entrypoint to run when starting the container
|
||||||
|
NetworkDisabled bool `json:",omitempty"` // Is network disabled
|
||||||
|
MacAddress string `json:",omitempty"` // Mac Address of the container
|
||||||
|
OnBuild []string // ONBUILD metadata that were defined on the image Dockerfile
|
||||||
|
Labels map[string]string // List of labels set to this container
|
||||||
|
StopSignal string `json:",omitempty"` // Signal to stop a container
|
||||||
|
StopTimeout *int `json:",omitempty"` // Timeout (in seconds) to stop a container
|
||||||
|
Shell strslice.StrSlice `json:",omitempty"` // Shell for shell-form of RUN, CMD, ENTRYPOINT
|
||||||
|
}
|
21
vendor/github.com/docker/docker/api/types/container/container_create.go
generated
vendored
Normal file
21
vendor/github.com/docker/docker/api/types/container/container_create.go
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
package container
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// DO NOT EDIT THIS FILE
|
||||||
|
// This file was generated by `swagger generate operation`
|
||||||
|
//
|
||||||
|
// See hack/generate-swagger-api.sh
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// ContainerCreateCreatedBody container create created body
|
||||||
|
// swagger:model ContainerCreateCreatedBody
|
||||||
|
type ContainerCreateCreatedBody struct {
|
||||||
|
|
||||||
|
// The ID of the created container
|
||||||
|
// Required: true
|
||||||
|
ID string `json:"Id"`
|
||||||
|
|
||||||
|
// Warnings encountered when creating the container
|
||||||
|
// Required: true
|
||||||
|
Warnings []string `json:"Warnings"`
|
||||||
|
}
|
17
vendor/github.com/docker/docker/api/types/container/container_update.go
generated
vendored
Normal file
17
vendor/github.com/docker/docker/api/types/container/container_update.go
generated
vendored
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
package container
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// DO NOT EDIT THIS FILE
|
||||||
|
// This file was generated by `swagger generate operation`
|
||||||
|
//
|
||||||
|
// See hack/generate-swagger-api.sh
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// ContainerUpdateOKBody container update o k body
|
||||||
|
// swagger:model ContainerUpdateOKBody
|
||||||
|
type ContainerUpdateOKBody struct {
|
||||||
|
|
||||||
|
// warnings
|
||||||
|
// Required: true
|
||||||
|
Warnings []string `json:"Warnings"`
|
||||||
|
}
|
17
vendor/github.com/docker/docker/api/types/container/container_wait.go
generated
vendored
Normal file
17
vendor/github.com/docker/docker/api/types/container/container_wait.go
generated
vendored
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
package container
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// DO NOT EDIT THIS FILE
|
||||||
|
// This file was generated by `swagger generate operation`
|
||||||
|
//
|
||||||
|
// See hack/generate-swagger-api.sh
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// ContainerWaitOKBody container wait o k body
|
||||||
|
// swagger:model ContainerWaitOKBody
|
||||||
|
type ContainerWaitOKBody struct {
|
||||||
|
|
||||||
|
// Exit code of the container
|
||||||
|
// Required: true
|
||||||
|
StatusCode int64 `json:"StatusCode"`
|
||||||
|
}
|
333
vendor/github.com/docker/docker/api/types/container/host_config.go
generated
vendored
Normal file
333
vendor/github.com/docker/docker/api/types/container/host_config.go
generated
vendored
Normal file
@ -0,0 +1,333 @@
|
|||||||
|
package container
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/docker/docker/api/types/blkiodev"
|
||||||
|
"github.com/docker/docker/api/types/mount"
|
||||||
|
"github.com/docker/docker/api/types/strslice"
|
||||||
|
"github.com/docker/go-connections/nat"
|
||||||
|
"github.com/docker/go-units"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NetworkMode represents the container network stack.
|
||||||
|
type NetworkMode string
|
||||||
|
|
||||||
|
// Isolation represents the isolation technology of a container. The supported
|
||||||
|
// values are platform specific
|
||||||
|
type Isolation string
|
||||||
|
|
||||||
|
// IsDefault indicates the default isolation technology of a container. On Linux this
|
||||||
|
// is the native driver. On Windows, this is a Windows Server Container.
|
||||||
|
func (i Isolation) IsDefault() bool {
|
||||||
|
return strings.ToLower(string(i)) == "default" || string(i) == ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// IpcMode represents the container ipc stack.
|
||||||
|
type IpcMode string
|
||||||
|
|
||||||
|
// IsPrivate indicates whether the container uses its private ipc stack.
|
||||||
|
func (n IpcMode) IsPrivate() bool {
|
||||||
|
return !(n.IsHost() || n.IsContainer())
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsHost indicates whether the container uses the host's ipc stack.
|
||||||
|
func (n IpcMode) IsHost() bool {
|
||||||
|
return n == "host"
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsContainer indicates whether the container uses a container's ipc stack.
|
||||||
|
func (n IpcMode) IsContainer() bool {
|
||||||
|
parts := strings.SplitN(string(n), ":", 2)
|
||||||
|
return len(parts) > 1 && parts[0] == "container"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Valid indicates whether the ipc stack is valid.
|
||||||
|
func (n IpcMode) Valid() bool {
|
||||||
|
parts := strings.Split(string(n), ":")
|
||||||
|
switch mode := parts[0]; mode {
|
||||||
|
case "", "host":
|
||||||
|
case "container":
|
||||||
|
if len(parts) != 2 || parts[1] == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Container returns the name of the container ipc stack is going to be used.
|
||||||
|
func (n IpcMode) Container() string {
|
||||||
|
parts := strings.SplitN(string(n), ":", 2)
|
||||||
|
if len(parts) > 1 {
|
||||||
|
return parts[1]
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// UsernsMode represents userns mode in the container.
|
||||||
|
type UsernsMode string
|
||||||
|
|
||||||
|
// IsHost indicates whether the container uses the host's userns.
|
||||||
|
func (n UsernsMode) IsHost() bool {
|
||||||
|
return n == "host"
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsPrivate indicates whether the container uses the a private userns.
|
||||||
|
func (n UsernsMode) IsPrivate() bool {
|
||||||
|
return !(n.IsHost())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Valid indicates whether the userns is valid.
|
||||||
|
func (n UsernsMode) Valid() bool {
|
||||||
|
parts := strings.Split(string(n), ":")
|
||||||
|
switch mode := parts[0]; mode {
|
||||||
|
case "", "host":
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// CgroupSpec represents the cgroup to use for the container.
|
||||||
|
type CgroupSpec string
|
||||||
|
|
||||||
|
// IsContainer indicates whether the container is using another container cgroup
|
||||||
|
func (c CgroupSpec) IsContainer() bool {
|
||||||
|
parts := strings.SplitN(string(c), ":", 2)
|
||||||
|
return len(parts) > 1 && parts[0] == "container"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Valid indicates whether the cgroup spec is valid.
|
||||||
|
func (c CgroupSpec) Valid() bool {
|
||||||
|
return c.IsContainer() || c == ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// Container returns the name of the container whose cgroup will be used.
|
||||||
|
func (c CgroupSpec) Container() string {
|
||||||
|
parts := strings.SplitN(string(c), ":", 2)
|
||||||
|
if len(parts) > 1 {
|
||||||
|
return parts[1]
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// UTSMode represents the UTS namespace of the container.
|
||||||
|
type UTSMode string
|
||||||
|
|
||||||
|
// IsPrivate indicates whether the container uses its private UTS namespace.
|
||||||
|
func (n UTSMode) IsPrivate() bool {
|
||||||
|
return !(n.IsHost())
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsHost indicates whether the container uses the host's UTS namespace.
|
||||||
|
func (n UTSMode) IsHost() bool {
|
||||||
|
return n == "host"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Valid indicates whether the UTS namespace is valid.
|
||||||
|
func (n UTSMode) Valid() bool {
|
||||||
|
parts := strings.Split(string(n), ":")
|
||||||
|
switch mode := parts[0]; mode {
|
||||||
|
case "", "host":
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// PidMode represents the pid namespace of the container.
|
||||||
|
type PidMode string
|
||||||
|
|
||||||
|
// IsPrivate indicates whether the container uses its own new pid namespace.
|
||||||
|
func (n PidMode) IsPrivate() bool {
|
||||||
|
return !(n.IsHost() || n.IsContainer())
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsHost indicates whether the container uses the host's pid namespace.
|
||||||
|
func (n PidMode) IsHost() bool {
|
||||||
|
return n == "host"
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsContainer indicates whether the container uses a container's pid namespace.
|
||||||
|
func (n PidMode) IsContainer() bool {
|
||||||
|
parts := strings.SplitN(string(n), ":", 2)
|
||||||
|
return len(parts) > 1 && parts[0] == "container"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Valid indicates whether the pid namespace is valid.
|
||||||
|
func (n PidMode) Valid() bool {
|
||||||
|
parts := strings.Split(string(n), ":")
|
||||||
|
switch mode := parts[0]; mode {
|
||||||
|
case "", "host":
|
||||||
|
case "container":
|
||||||
|
if len(parts) != 2 || parts[1] == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Container returns the name of the container whose pid namespace is going to be used.
|
||||||
|
func (n PidMode) Container() string {
|
||||||
|
parts := strings.SplitN(string(n), ":", 2)
|
||||||
|
if len(parts) > 1 {
|
||||||
|
return parts[1]
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeviceMapping represents the device mapping between the host and the container.
|
||||||
|
type DeviceMapping struct {
|
||||||
|
PathOnHost string
|
||||||
|
PathInContainer string
|
||||||
|
CgroupPermissions string
|
||||||
|
}
|
||||||
|
|
||||||
|
// RestartPolicy represents the restart policies of the container.
|
||||||
|
type RestartPolicy struct {
|
||||||
|
Name string
|
||||||
|
MaximumRetryCount int
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsNone indicates whether the container has the "no" restart policy.
|
||||||
|
// This means the container will not automatically restart when exiting.
|
||||||
|
func (rp *RestartPolicy) IsNone() bool {
|
||||||
|
return rp.Name == "no" || rp.Name == ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsAlways indicates whether the container has the "always" restart policy.
|
||||||
|
// This means the container will automatically restart regardless of the exit status.
|
||||||
|
func (rp *RestartPolicy) IsAlways() bool {
|
||||||
|
return rp.Name == "always"
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsOnFailure indicates whether the container has the "on-failure" restart policy.
|
||||||
|
// This means the container will automatically restart of exiting with a non-zero exit status.
|
||||||
|
func (rp *RestartPolicy) IsOnFailure() bool {
|
||||||
|
return rp.Name == "on-failure"
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsUnlessStopped indicates whether the container has the
|
||||||
|
// "unless-stopped" restart policy. This means the container will
|
||||||
|
// automatically restart unless user has put it to stopped state.
|
||||||
|
func (rp *RestartPolicy) IsUnlessStopped() bool {
|
||||||
|
return rp.Name == "unless-stopped"
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsSame compares two RestartPolicy to see if they are the same
|
||||||
|
func (rp *RestartPolicy) IsSame(tp *RestartPolicy) bool {
|
||||||
|
return rp.Name == tp.Name && rp.MaximumRetryCount == tp.MaximumRetryCount
|
||||||
|
}
|
||||||
|
|
||||||
|
// LogConfig represents the logging configuration of the container.
|
||||||
|
type LogConfig struct {
|
||||||
|
Type string
|
||||||
|
Config map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resources contains container's resources (cgroups config, ulimits...)
|
||||||
|
type Resources struct {
|
||||||
|
// Applicable to all platforms
|
||||||
|
CPUShares int64 `json:"CpuShares"` // CPU shares (relative weight vs. other containers)
|
||||||
|
Memory int64 // Memory limit (in bytes)
|
||||||
|
NanoCPUs int64 `json:"NanoCpus"` // CPU quota in units of 10<sup>-9</sup> CPUs.
|
||||||
|
|
||||||
|
// Applicable to UNIX platforms
|
||||||
|
CgroupParent string // Parent cgroup.
|
||||||
|
BlkioWeight uint16 // Block IO weight (relative weight vs. other containers)
|
||||||
|
BlkioWeightDevice []*blkiodev.WeightDevice
|
||||||
|
BlkioDeviceReadBps []*blkiodev.ThrottleDevice
|
||||||
|
BlkioDeviceWriteBps []*blkiodev.ThrottleDevice
|
||||||
|
BlkioDeviceReadIOps []*blkiodev.ThrottleDevice
|
||||||
|
BlkioDeviceWriteIOps []*blkiodev.ThrottleDevice
|
||||||
|
CPUPeriod int64 `json:"CpuPeriod"` // CPU CFS (Completely Fair Scheduler) period
|
||||||
|
CPUQuota int64 `json:"CpuQuota"` // CPU CFS (Completely Fair Scheduler) quota
|
||||||
|
CPURealtimePeriod int64 `json:"CpuRealtimePeriod"` // CPU real-time period
|
||||||
|
CPURealtimeRuntime int64 `json:"CpuRealtimeRuntime"` // CPU real-time runtime
|
||||||
|
CpusetCpus string // CpusetCpus 0-2, 0,1
|
||||||
|
CpusetMems string // CpusetMems 0-2, 0,1
|
||||||
|
Devices []DeviceMapping // List of devices to map inside the container
|
||||||
|
DiskQuota int64 // Disk limit (in bytes)
|
||||||
|
KernelMemory int64 // Kernel memory limit (in bytes)
|
||||||
|
MemoryReservation int64 // Memory soft limit (in bytes)
|
||||||
|
MemorySwap int64 // Total memory usage (memory + swap); set `-1` to enable unlimited swap
|
||||||
|
MemorySwappiness *int64 // Tuning container memory swappiness behaviour
|
||||||
|
OomKillDisable *bool // Whether to disable OOM Killer or not
|
||||||
|
PidsLimit int64 // Setting pids limit for a container
|
||||||
|
Ulimits []*units.Ulimit // List of ulimits to be set in the container
|
||||||
|
|
||||||
|
// Applicable to Windows
|
||||||
|
CPUCount int64 `json:"CpuCount"` // CPU count
|
||||||
|
CPUPercent int64 `json:"CpuPercent"` // CPU percent
|
||||||
|
IOMaximumIOps uint64 // Maximum IOps for the container system drive
|
||||||
|
IOMaximumBandwidth uint64 // Maximum IO in bytes per second for the container system drive
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateConfig holds the mutable attributes of a Container.
|
||||||
|
// Those attributes can be updated at runtime.
|
||||||
|
type UpdateConfig struct {
|
||||||
|
// Contains container's resources (cgroups, ulimits)
|
||||||
|
Resources
|
||||||
|
RestartPolicy RestartPolicy
|
||||||
|
}
|
||||||
|
|
||||||
|
// HostConfig the non-portable Config structure of a container.
|
||||||
|
// Here, "non-portable" means "dependent of the host we are running on".
|
||||||
|
// Portable information *should* appear in Config.
|
||||||
|
type HostConfig struct {
|
||||||
|
// Applicable to all platforms
|
||||||
|
Binds []string // List of volume bindings for this container
|
||||||
|
ContainerIDFile string // File (path) where the containerId is written
|
||||||
|
LogConfig LogConfig // Configuration of the logs for this container
|
||||||
|
NetworkMode NetworkMode // Network mode to use for the container
|
||||||
|
PortBindings nat.PortMap // Port mapping between the exposed port (container) and the host
|
||||||
|
RestartPolicy RestartPolicy // Restart policy to be used for the container
|
||||||
|
AutoRemove bool // Automatically remove container when it exits
|
||||||
|
VolumeDriver string // Name of the volume driver used to mount volumes
|
||||||
|
VolumesFrom []string // List of volumes to take from other container
|
||||||
|
|
||||||
|
// Applicable to UNIX platforms
|
||||||
|
CapAdd strslice.StrSlice // List of kernel capabilities to add to the container
|
||||||
|
CapDrop strslice.StrSlice // List of kernel capabilities to remove from the container
|
||||||
|
DNS []string `json:"Dns"` // List of DNS server to lookup
|
||||||
|
DNSOptions []string `json:"DnsOptions"` // List of DNSOption to look for
|
||||||
|
DNSSearch []string `json:"DnsSearch"` // List of DNSSearch to look for
|
||||||
|
ExtraHosts []string // List of extra hosts
|
||||||
|
GroupAdd []string // List of additional groups that the container process will run as
|
||||||
|
IpcMode IpcMode // IPC namespace to use for the container
|
||||||
|
Cgroup CgroupSpec // Cgroup to use for the container
|
||||||
|
Links []string // List of links (in the name:alias form)
|
||||||
|
OomScoreAdj int // Container preference for OOM-killing
|
||||||
|
PidMode PidMode // PID namespace to use for the container
|
||||||
|
Privileged bool // Is the container in privileged mode
|
||||||
|
PublishAllPorts bool // Should docker publish all exposed port for the container
|
||||||
|
ReadonlyRootfs bool // Is the container root filesystem in read-only
|
||||||
|
SecurityOpt []string // List of string values to customize labels for MLS systems, such as SELinux.
|
||||||
|
StorageOpt map[string]string `json:",omitempty"` // Storage driver options per container.
|
||||||
|
Tmpfs map[string]string `json:",omitempty"` // List of tmpfs (mounts) used for the container
|
||||||
|
UTSMode UTSMode // UTS namespace to use for the container
|
||||||
|
UsernsMode UsernsMode // The user namespace to use for the container
|
||||||
|
ShmSize int64 // Total shm memory usage
|
||||||
|
Sysctls map[string]string `json:",omitempty"` // List of Namespaced sysctls used for the container
|
||||||
|
Runtime string `json:",omitempty"` // Runtime to use with this container
|
||||||
|
|
||||||
|
// Applicable to Windows
|
||||||
|
ConsoleSize [2]uint // Initial console size (height,width)
|
||||||
|
Isolation Isolation // Isolation technology of the container (e.g. default, hyperv)
|
||||||
|
|
||||||
|
// Contains container's resources (cgroups, ulimits)
|
||||||
|
Resources
|
||||||
|
|
||||||
|
// Mounts specs used by the container
|
||||||
|
Mounts []mount.Mount `json:",omitempty"`
|
||||||
|
|
||||||
|
// Run a custom init inside the container, if null, use the daemon's configured settings
|
||||||
|
Init *bool `json:",omitempty"`
|
||||||
|
|
||||||
|
// Custom init path
|
||||||
|
InitPath string `json:",omitempty"`
|
||||||
|
}
|
81
vendor/github.com/docker/docker/api/types/container/hostconfig_unix.go
generated
vendored
Normal file
81
vendor/github.com/docker/docker/api/types/container/hostconfig_unix.go
generated
vendored
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
// +build !windows
|
||||||
|
|
||||||
|
package container
|
||||||
|
|
||||||
|
import "strings"
|
||||||
|
|
||||||
|
// IsValid indicates if an isolation technology is valid
|
||||||
|
func (i Isolation) IsValid() bool {
|
||||||
|
return i.IsDefault()
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsPrivate indicates whether container uses its private network stack.
|
||||||
|
func (n NetworkMode) IsPrivate() bool {
|
||||||
|
return !(n.IsHost() || n.IsContainer())
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsDefault indicates whether container uses the default network stack.
|
||||||
|
func (n NetworkMode) IsDefault() bool {
|
||||||
|
return n == "default"
|
||||||
|
}
|
||||||
|
|
||||||
|
// NetworkName returns the name of the network stack.
|
||||||
|
func (n NetworkMode) NetworkName() string {
|
||||||
|
if n.IsBridge() {
|
||||||
|
return "bridge"
|
||||||
|
} else if n.IsHost() {
|
||||||
|
return "host"
|
||||||
|
} else if n.IsContainer() {
|
||||||
|
return "container"
|
||||||
|
} else if n.IsNone() {
|
||||||
|
return "none"
|
||||||
|
} else if n.IsDefault() {
|
||||||
|
return "default"
|
||||||
|
} else if n.IsUserDefined() {
|
||||||
|
return n.UserDefined()
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsBridge indicates whether container uses the bridge network stack
|
||||||
|
func (n NetworkMode) IsBridge() bool {
|
||||||
|
return n == "bridge"
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsHost indicates whether container uses the host network stack.
|
||||||
|
func (n NetworkMode) IsHost() bool {
|
||||||
|
return n == "host"
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsContainer indicates whether container uses a container network stack.
|
||||||
|
func (n NetworkMode) IsContainer() bool {
|
||||||
|
parts := strings.SplitN(string(n), ":", 2)
|
||||||
|
return len(parts) > 1 && parts[0] == "container"
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsNone indicates whether container isn't using a network stack.
|
||||||
|
func (n NetworkMode) IsNone() bool {
|
||||||
|
return n == "none"
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConnectedContainer is the id of the container which network this container is connected to.
|
||||||
|
func (n NetworkMode) ConnectedContainer() string {
|
||||||
|
parts := strings.SplitN(string(n), ":", 2)
|
||||||
|
if len(parts) > 1 {
|
||||||
|
return parts[1]
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsUserDefined indicates user-created network
|
||||||
|
func (n NetworkMode) IsUserDefined() bool {
|
||||||
|
return !n.IsDefault() && !n.IsBridge() && !n.IsHost() && !n.IsNone() && !n.IsContainer()
|
||||||
|
}
|
||||||
|
|
||||||
|
//UserDefined indicates user-created network
|
||||||
|
func (n NetworkMode) UserDefined() string {
|
||||||
|
if n.IsUserDefined() {
|
||||||
|
return string(n)
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
87
vendor/github.com/docker/docker/api/types/container/hostconfig_windows.go
generated
vendored
Normal file
87
vendor/github.com/docker/docker/api/types/container/hostconfig_windows.go
generated
vendored
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
package container
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// IsDefault indicates whether container uses the default network stack.
|
||||||
|
func (n NetworkMode) IsDefault() bool {
|
||||||
|
return n == "default"
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsNone indicates whether container isn't using a network stack.
|
||||||
|
func (n NetworkMode) IsNone() bool {
|
||||||
|
return n == "none"
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsContainer indicates whether container uses a container network stack.
|
||||||
|
// Returns false as windows doesn't support this mode
|
||||||
|
func (n NetworkMode) IsContainer() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsBridge indicates whether container uses the bridge network stack
|
||||||
|
// in windows it is given the name NAT
|
||||||
|
func (n NetworkMode) IsBridge() bool {
|
||||||
|
return n == "nat"
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsHost indicates whether container uses the host network stack.
|
||||||
|
// returns false as this is not supported by windows
|
||||||
|
func (n NetworkMode) IsHost() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsPrivate indicates whether container uses its private network stack.
|
||||||
|
func (n NetworkMode) IsPrivate() bool {
|
||||||
|
return !(n.IsHost() || n.IsContainer())
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConnectedContainer is the id of the container which network this container is connected to.
|
||||||
|
// Returns blank string on windows
|
||||||
|
func (n NetworkMode) ConnectedContainer() string {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsUserDefined indicates user-created network
|
||||||
|
func (n NetworkMode) IsUserDefined() bool {
|
||||||
|
return !n.IsDefault() && !n.IsNone() && !n.IsBridge()
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsHyperV indicates the use of a Hyper-V partition for isolation
|
||||||
|
func (i Isolation) IsHyperV() bool {
|
||||||
|
return strings.ToLower(string(i)) == "hyperv"
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsProcess indicates the use of process isolation
|
||||||
|
func (i Isolation) IsProcess() bool {
|
||||||
|
return strings.ToLower(string(i)) == "process"
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsValid indicates if an isolation technology is valid
|
||||||
|
func (i Isolation) IsValid() bool {
|
||||||
|
return i.IsDefault() || i.IsHyperV() || i.IsProcess()
|
||||||
|
}
|
||||||
|
|
||||||
|
// NetworkName returns the name of the network stack.
|
||||||
|
func (n NetworkMode) NetworkName() string {
|
||||||
|
if n.IsDefault() {
|
||||||
|
return "default"
|
||||||
|
} else if n.IsBridge() {
|
||||||
|
return "nat"
|
||||||
|
} else if n.IsNone() {
|
||||||
|
return "none"
|
||||||
|
} else if n.IsUserDefined() {
|
||||||
|
return n.UserDefined()
|
||||||
|
}
|
||||||
|
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
//UserDefined indicates user-created network
|
||||||
|
func (n NetworkMode) UserDefined() string {
|
||||||
|
if n.IsUserDefined() {
|
||||||
|
return string(n)
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
13
vendor/github.com/docker/docker/api/types/error_response.go
generated
vendored
Normal file
13
vendor/github.com/docker/docker/api/types/error_response.go
generated
vendored
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
package types
|
||||||
|
|
||||||
|
// This file was generated by the swagger tool.
|
||||||
|
// Editing this file might prove futile when you re-run the swagger generate command
|
||||||
|
|
||||||
|
// ErrorResponse Represents an error.
|
||||||
|
// swagger:model ErrorResponse
|
||||||
|
type ErrorResponse struct {
|
||||||
|
|
||||||
|
// The error message.
|
||||||
|
// Required: true
|
||||||
|
Message string `json:"message"`
|
||||||
|
}
|
42
vendor/github.com/docker/docker/api/types/events/events.go
generated
vendored
Normal file
42
vendor/github.com/docker/docker/api/types/events/events.go
generated
vendored
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
package events
|
||||||
|
|
||||||
|
const (
|
||||||
|
// ContainerEventType is the event type that containers generate
|
||||||
|
ContainerEventType = "container"
|
||||||
|
// DaemonEventType is the event type that daemon generate
|
||||||
|
DaemonEventType = "daemon"
|
||||||
|
// ImageEventType is the event type that images generate
|
||||||
|
ImageEventType = "image"
|
||||||
|
// NetworkEventType is the event type that networks generate
|
||||||
|
NetworkEventType = "network"
|
||||||
|
// PluginEventType is the event type that plugins generate
|
||||||
|
PluginEventType = "plugin"
|
||||||
|
// VolumeEventType is the event type that volumes generate
|
||||||
|
VolumeEventType = "volume"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Actor describes something that generates events,
|
||||||
|
// like a container, or a network, or a volume.
|
||||||
|
// It has a defined name and a set or attributes.
|
||||||
|
// The container attributes are its labels, other actors
|
||||||
|
// can generate these attributes from other properties.
|
||||||
|
type Actor struct {
|
||||||
|
ID string
|
||||||
|
Attributes map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Message represents the information an event contains
|
||||||
|
type Message struct {
|
||||||
|
// Deprecated information from JSONMessage.
|
||||||
|
// With data only in container events.
|
||||||
|
Status string `json:"status,omitempty"`
|
||||||
|
ID string `json:"id,omitempty"`
|
||||||
|
From string `json:"from,omitempty"`
|
||||||
|
|
||||||
|
Type string
|
||||||
|
Action string
|
||||||
|
Actor Actor
|
||||||
|
|
||||||
|
Time int64 `json:"time,omitempty"`
|
||||||
|
TimeNano int64 `json:"timeNano,omitempty"`
|
||||||
|
}
|
310
vendor/github.com/docker/docker/api/types/filters/parse.go
generated
vendored
Normal file
310
vendor/github.com/docker/docker/api/types/filters/parse.go
generated
vendored
Normal file
@ -0,0 +1,310 @@
|
|||||||
|
// Package filters provides helper function to parse and handle command line
|
||||||
|
// filter, used for example in docker ps or docker images commands.
|
||||||
|
package filters
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/docker/docker/api/types/versions"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Args stores filter arguments as map key:{map key: bool}.
|
||||||
|
// It contains an aggregation of the map of arguments (which are in the form
|
||||||
|
// of -f 'key=value') based on the key, and stores values for the same key
|
||||||
|
// in a map with string keys and boolean values.
|
||||||
|
// e.g given -f 'label=label1=1' -f 'label=label2=2' -f 'image.name=ubuntu'
|
||||||
|
// the args will be {"image.name":{"ubuntu":true},"label":{"label1=1":true,"label2=2":true}}
|
||||||
|
type Args struct {
|
||||||
|
fields map[string]map[string]bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewArgs initializes a new Args struct.
|
||||||
|
func NewArgs() Args {
|
||||||
|
return Args{fields: map[string]map[string]bool{}}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseFlag parses the argument to the filter flag. Like
|
||||||
|
//
|
||||||
|
// `docker ps -f 'created=today' -f 'image.name=ubuntu*'`
|
||||||
|
//
|
||||||
|
// If prev map is provided, then it is appended to, and returned. By default a new
|
||||||
|
// map is created.
|
||||||
|
func ParseFlag(arg string, prev Args) (Args, error) {
|
||||||
|
filters := prev
|
||||||
|
if len(arg) == 0 {
|
||||||
|
return filters, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.Contains(arg, "=") {
|
||||||
|
return filters, ErrBadFormat
|
||||||
|
}
|
||||||
|
|
||||||
|
f := strings.SplitN(arg, "=", 2)
|
||||||
|
|
||||||
|
name := strings.ToLower(strings.TrimSpace(f[0]))
|
||||||
|
value := strings.TrimSpace(f[1])
|
||||||
|
|
||||||
|
filters.Add(name, value)
|
||||||
|
|
||||||
|
return filters, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrBadFormat is an error returned in case of bad format for a filter.
|
||||||
|
var ErrBadFormat = errors.New("bad format of filter (expected name=value)")
|
||||||
|
|
||||||
|
// ToParam packs the Args into a string for easy transport from client to server.
|
||||||
|
func ToParam(a Args) (string, error) {
|
||||||
|
// this way we don't URL encode {}, just empty space
|
||||||
|
if a.Len() == 0 {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
buf, err := json.Marshal(a.fields)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return string(buf), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToParamWithVersion packs the Args into a string for easy transport from client to server.
|
||||||
|
// The generated string will depend on the specified version (corresponding to the API version).
|
||||||
|
func ToParamWithVersion(version string, a Args) (string, error) {
|
||||||
|
// this way we don't URL encode {}, just empty space
|
||||||
|
if a.Len() == 0 {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// for daemons older than v1.10, filter must be of the form map[string][]string
|
||||||
|
var buf []byte
|
||||||
|
var err error
|
||||||
|
if version != "" && versions.LessThan(version, "1.22") {
|
||||||
|
buf, err = json.Marshal(convertArgsToSlice(a.fields))
|
||||||
|
} else {
|
||||||
|
buf, err = json.Marshal(a.fields)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return string(buf), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromParam unpacks the filter Args.
|
||||||
|
func FromParam(p string) (Args, error) {
|
||||||
|
if len(p) == 0 {
|
||||||
|
return NewArgs(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
r := strings.NewReader(p)
|
||||||
|
d := json.NewDecoder(r)
|
||||||
|
|
||||||
|
m := map[string]map[string]bool{}
|
||||||
|
if err := d.Decode(&m); err != nil {
|
||||||
|
r.Seek(0, 0)
|
||||||
|
|
||||||
|
// Allow parsing old arguments in slice format.
|
||||||
|
// Because other libraries might be sending them in this format.
|
||||||
|
deprecated := map[string][]string{}
|
||||||
|
if deprecatedErr := d.Decode(&deprecated); deprecatedErr == nil {
|
||||||
|
m = deprecatedArgs(deprecated)
|
||||||
|
} else {
|
||||||
|
return NewArgs(), err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Args{m}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get returns the list of values associates with a field.
|
||||||
|
// It returns a slice of strings to keep backwards compatibility with old code.
|
||||||
|
func (filters Args) Get(field string) []string {
|
||||||
|
values := filters.fields[field]
|
||||||
|
if values == nil {
|
||||||
|
return make([]string, 0)
|
||||||
|
}
|
||||||
|
slice := make([]string, 0, len(values))
|
||||||
|
for key := range values {
|
||||||
|
slice = append(slice, key)
|
||||||
|
}
|
||||||
|
return slice
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add adds a new value to a filter field.
|
||||||
|
func (filters Args) Add(name, value string) {
|
||||||
|
if _, ok := filters.fields[name]; ok {
|
||||||
|
filters.fields[name][value] = true
|
||||||
|
} else {
|
||||||
|
filters.fields[name] = map[string]bool{value: true}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Del removes a value from a filter field.
|
||||||
|
func (filters Args) Del(name, value string) {
|
||||||
|
if _, ok := filters.fields[name]; ok {
|
||||||
|
delete(filters.fields[name], value)
|
||||||
|
if len(filters.fields[name]) == 0 {
|
||||||
|
delete(filters.fields, name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Len returns the number of fields in the arguments.
|
||||||
|
func (filters Args) Len() int {
|
||||||
|
return len(filters.fields)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MatchKVList returns true if the values for the specified field matches the ones
|
||||||
|
// from the sources.
|
||||||
|
// e.g. given Args are {'label': {'label1=1','label2=1'}, 'image.name', {'ubuntu'}},
|
||||||
|
// field is 'label' and sources are {'label1': '1', 'label2': '2'}
|
||||||
|
// it returns true.
|
||||||
|
func (filters Args) MatchKVList(field string, sources map[string]string) bool {
|
||||||
|
fieldValues := filters.fields[field]
|
||||||
|
|
||||||
|
//do not filter if there is no filter set or cannot determine filter
|
||||||
|
if len(fieldValues) == 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(sources) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for name2match := range fieldValues {
|
||||||
|
testKV := strings.SplitN(name2match, "=", 2)
|
||||||
|
|
||||||
|
v, ok := sources[testKV[0]]
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if len(testKV) == 2 && testKV[1] != v {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Match returns true if the values for the specified field matches the source string
|
||||||
|
// e.g. given Args are {'label': {'label1=1','label2=1'}, 'image.name', {'ubuntu'}},
|
||||||
|
// field is 'image.name' and source is 'ubuntu'
|
||||||
|
// it returns true.
|
||||||
|
func (filters Args) Match(field, source string) bool {
|
||||||
|
if filters.ExactMatch(field, source) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldValues := filters.fields[field]
|
||||||
|
for name2match := range fieldValues {
|
||||||
|
match, err := regexp.MatchString(name2match, source)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if match {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExactMatch returns true if the source matches exactly one of the filters.
|
||||||
|
func (filters Args) ExactMatch(field, source string) bool {
|
||||||
|
fieldValues, ok := filters.fields[field]
|
||||||
|
//do not filter if there is no filter set or cannot determine filter
|
||||||
|
if !ok || len(fieldValues) == 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// try to match full name value to avoid O(N) regular expression matching
|
||||||
|
return fieldValues[source]
|
||||||
|
}
|
||||||
|
|
||||||
|
// UniqueExactMatch returns true if there is only one filter and the source matches exactly this one.
|
||||||
|
func (filters Args) UniqueExactMatch(field, source string) bool {
|
||||||
|
fieldValues := filters.fields[field]
|
||||||
|
//do not filter if there is no filter set or cannot determine filter
|
||||||
|
if len(fieldValues) == 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if len(filters.fields[field]) != 1 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// try to match full name value to avoid O(N) regular expression matching
|
||||||
|
return fieldValues[source]
|
||||||
|
}
|
||||||
|
|
||||||
|
// FuzzyMatch returns true if the source matches exactly one of the filters,
|
||||||
|
// or the source has one of the filters as a prefix.
|
||||||
|
func (filters Args) FuzzyMatch(field, source string) bool {
|
||||||
|
if filters.ExactMatch(field, source) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldValues := filters.fields[field]
|
||||||
|
for prefix := range fieldValues {
|
||||||
|
if strings.HasPrefix(source, prefix) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Include returns true if the name of the field to filter is in the filters.
|
||||||
|
func (filters Args) Include(field string) bool {
|
||||||
|
_, ok := filters.fields[field]
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate ensures that all the fields in the filter are valid.
|
||||||
|
// It returns an error as soon as it finds an invalid field.
|
||||||
|
func (filters Args) Validate(accepted map[string]bool) error {
|
||||||
|
for name := range filters.fields {
|
||||||
|
if !accepted[name] {
|
||||||
|
return fmt.Errorf("Invalid filter '%s'", name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// WalkValues iterates over the list of filtered values for a field.
|
||||||
|
// It stops the iteration if it finds an error and it returns that error.
|
||||||
|
func (filters Args) WalkValues(field string, op func(value string) error) error {
|
||||||
|
if _, ok := filters.fields[field]; !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
for v := range filters.fields[field] {
|
||||||
|
if err := op(v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func deprecatedArgs(d map[string][]string) map[string]map[string]bool {
|
||||||
|
m := map[string]map[string]bool{}
|
||||||
|
for k, v := range d {
|
||||||
|
values := map[string]bool{}
|
||||||
|
for _, vv := range v {
|
||||||
|
values[vv] = true
|
||||||
|
}
|
||||||
|
m[k] = values
|
||||||
|
}
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
func convertArgsToSlice(f map[string]map[string]bool) map[string][]string {
|
||||||
|
m := map[string][]string{}
|
||||||
|
for k, v := range f {
|
||||||
|
values := []string{}
|
||||||
|
for kk := range v {
|
||||||
|
if v[kk] {
|
||||||
|
values = append(values, kk)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m[k] = values
|
||||||
|
}
|
||||||
|
return m
|
||||||
|
}
|
13
vendor/github.com/docker/docker/api/types/id_response.go
generated
vendored
Normal file
13
vendor/github.com/docker/docker/api/types/id_response.go
generated
vendored
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
package types
|
||||||
|
|
||||||
|
// This file was generated by the swagger tool.
|
||||||
|
// Editing this file might prove futile when you re-run the swagger generate command
|
||||||
|
|
||||||
|
// IDResponse Response to an API call that returns just an Id
|
||||||
|
// swagger:model IdResponse
|
||||||
|
type IDResponse struct {
|
||||||
|
|
||||||
|
// The id of the newly created object.
|
||||||
|
// Required: true
|
||||||
|
ID string `json:"Id"`
|
||||||
|
}
|
49
vendor/github.com/docker/docker/api/types/image_summary.go
generated
vendored
Normal file
49
vendor/github.com/docker/docker/api/types/image_summary.go
generated
vendored
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
package types
|
||||||
|
|
||||||
|
// This file was generated by the swagger tool.
|
||||||
|
// Editing this file might prove futile when you re-run the swagger generate command
|
||||||
|
|
||||||
|
// ImageSummary image summary
|
||||||
|
// swagger:model ImageSummary
|
||||||
|
type ImageSummary struct {
|
||||||
|
|
||||||
|
// containers
|
||||||
|
// Required: true
|
||||||
|
Containers int64 `json:"Containers"`
|
||||||
|
|
||||||
|
// created
|
||||||
|
// Required: true
|
||||||
|
Created int64 `json:"Created"`
|
||||||
|
|
||||||
|
// Id
|
||||||
|
// Required: true
|
||||||
|
ID string `json:"Id"`
|
||||||
|
|
||||||
|
// labels
|
||||||
|
// Required: true
|
||||||
|
Labels map[string]string `json:"Labels"`
|
||||||
|
|
||||||
|
// parent Id
|
||||||
|
// Required: true
|
||||||
|
ParentID string `json:"ParentId"`
|
||||||
|
|
||||||
|
// repo digests
|
||||||
|
// Required: true
|
||||||
|
RepoDigests []string `json:"RepoDigests"`
|
||||||
|
|
||||||
|
// repo tags
|
||||||
|
// Required: true
|
||||||
|
RepoTags []string `json:"RepoTags"`
|
||||||
|
|
||||||
|
// shared size
|
||||||
|
// Required: true
|
||||||
|
SharedSize int64 `json:"SharedSize"`
|
||||||
|
|
||||||
|
// size
|
||||||
|
// Required: true
|
||||||
|
Size int64 `json:"Size"`
|
||||||
|
|
||||||
|
// virtual size
|
||||||
|
// Required: true
|
||||||
|
VirtualSize int64 `json:"VirtualSize"`
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user