2021-05-07 23:45:15 +02:00
|
|
|
package state
|
2021-01-30 02:16:22 +01:00
|
|
|
|
|
|
|
import (
|
2021-09-10 23:10:39 +02:00
|
|
|
"crypto/sha256"
|
2021-01-30 02:16:22 +01:00
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
2021-04-09 08:52:17 +02:00
|
|
|
"io/ioutil"
|
2021-05-07 23:45:15 +02:00
|
|
|
"path"
|
2021-04-03 02:14:01 +02:00
|
|
|
"path/filepath"
|
2021-05-19 04:15:17 +02:00
|
|
|
"strings"
|
2021-02-07 08:36:21 +01:00
|
|
|
|
2021-04-09 08:52:17 +02:00
|
|
|
"cuelang.org/go/cue"
|
|
|
|
|
2021-05-26 01:53:26 +02:00
|
|
|
"go.dagger.io/dagger/compiler"
|
2021-01-30 02:16:22 +01:00
|
|
|
)
|
|
|
|
|
2021-03-24 18:37:50 +01:00
|
|
|
// An input is a value or artifact supplied by the user.
|
|
|
|
//
|
|
|
|
// - A value is any structured data which can be encoded as cue.
|
|
|
|
//
|
|
|
|
// - An artifact is a piece of data, like a source code checkout,
|
|
|
|
// binary bundle, docker image, database backup etc.
|
|
|
|
//
|
|
|
|
// Artifacts can be passed as inputs, generated dynamically from
|
|
|
|
// other inputs, and received as outputs.
|
|
|
|
// Under the hood, an artifact is encoded as a LLB pipeline, and
|
|
|
|
// attached to the cue configuration as a
|
|
|
|
//
|
2021-03-26 22:11:54 +01:00
|
|
|
|
|
|
|
type Input struct {
|
2021-05-07 23:45:15 +02:00
|
|
|
Dir *dirInput `yaml:"dir,omitempty"`
|
|
|
|
Git *gitInput `yaml:"git,omitempty"`
|
|
|
|
Docker *dockerInput `yaml:"docker,omitempty"`
|
2021-05-17 20:12:46 +02:00
|
|
|
Secret *secretInput `yaml:"secret,omitempty"`
|
2021-05-07 23:45:15 +02:00
|
|
|
Text *textInput `yaml:"text,omitempty"`
|
|
|
|
JSON *jsonInput `yaml:"json,omitempty"`
|
|
|
|
YAML *yamlInput `yaml:"yaml,omitempty"`
|
|
|
|
File *fileInput `yaml:"file,omitempty"`
|
2021-08-24 16:00:53 +02:00
|
|
|
Bool *boolInput `yaml:"bool,omitempty"`
|
2021-09-17 02:18:05 +02:00
|
|
|
Socket *socketInput `yaml:"socket,omitempty"`
|
2021-03-26 22:11:54 +01:00
|
|
|
}
|
|
|
|
|
2021-05-26 03:56:16 +02:00
|
|
|
func (i Input) Compile(key string, state *State) (*compiler.Value, error) {
|
2021-05-17 20:12:46 +02:00
|
|
|
switch {
|
|
|
|
case i.Dir != nil:
|
2021-05-26 03:56:16 +02:00
|
|
|
return i.Dir.Compile(key, state)
|
2021-05-17 20:12:46 +02:00
|
|
|
case i.Git != nil:
|
2021-05-26 03:56:16 +02:00
|
|
|
return i.Git.Compile(key, state)
|
2021-05-17 20:12:46 +02:00
|
|
|
case i.Docker != nil:
|
2021-05-26 03:56:16 +02:00
|
|
|
return i.Docker.Compile(key, state)
|
2021-05-17 20:12:46 +02:00
|
|
|
case i.Text != nil:
|
2021-05-26 03:56:16 +02:00
|
|
|
return i.Text.Compile(key, state)
|
2021-05-17 20:12:46 +02:00
|
|
|
case i.Secret != nil:
|
2021-05-26 03:56:16 +02:00
|
|
|
return i.Secret.Compile(key, state)
|
2021-05-17 20:12:46 +02:00
|
|
|
case i.JSON != nil:
|
2021-05-26 03:56:16 +02:00
|
|
|
return i.JSON.Compile(key, state)
|
2021-05-17 20:12:46 +02:00
|
|
|
case i.YAML != nil:
|
2021-05-26 03:56:16 +02:00
|
|
|
return i.YAML.Compile(key, state)
|
2021-05-17 20:12:46 +02:00
|
|
|
case i.File != nil:
|
2021-05-26 03:56:16 +02:00
|
|
|
return i.File.Compile(key, state)
|
2021-08-24 16:00:53 +02:00
|
|
|
case i.Bool != nil:
|
|
|
|
return i.Bool.Compile(key, state)
|
2021-09-17 02:18:05 +02:00
|
|
|
case i.Socket != nil:
|
|
|
|
return i.Socket.Compile(key, state)
|
2021-03-26 22:11:54 +01:00
|
|
|
default:
|
2021-05-17 20:12:46 +02:00
|
|
|
return nil, fmt.Errorf("input has not been set")
|
2021-03-26 22:11:54 +01:00
|
|
|
}
|
2021-03-24 18:37:50 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// An input artifact loaded from a local directory
|
2021-05-29 01:04:16 +02:00
|
|
|
func DirInput(path string, include []string, exclude []string) Input {
|
2021-03-26 22:11:54 +01:00
|
|
|
return Input{
|
|
|
|
Dir: &dirInput{
|
|
|
|
Path: path,
|
|
|
|
Include: include,
|
2021-05-29 01:04:16 +02:00
|
|
|
Exclude: exclude,
|
2021-03-26 22:11:54 +01:00
|
|
|
},
|
2021-03-24 18:37:50 +01:00
|
|
|
}
|
2021-01-30 02:16:22 +01:00
|
|
|
}
|
|
|
|
|
2021-03-24 18:37:50 +01:00
|
|
|
type dirInput struct {
|
2021-05-29 01:04:16 +02:00
|
|
|
Path string `yaml:"path,omitempty"`
|
|
|
|
Include []string `yaml:"include,omitempty"`
|
|
|
|
Exclude []string `yaml:"exclude,omitempty"`
|
2021-01-30 02:16:22 +01:00
|
|
|
}
|
|
|
|
|
2021-05-26 03:56:16 +02:00
|
|
|
func (dir dirInput) Compile(_ string, state *State) (*compiler.Value, error) {
|
2021-03-24 18:37:50 +01:00
|
|
|
// FIXME: serialize an intermediate struct, instead of generating cue source
|
2021-04-02 08:08:26 +02:00
|
|
|
|
|
|
|
// json.Marshal([]string{}) returns []byte("null"), which wreaks havoc
|
|
|
|
// in Cue because `null` is not a `[...string]`
|
|
|
|
includeLLB := []byte("[]")
|
|
|
|
if len(dir.Include) > 0 {
|
|
|
|
var err error
|
|
|
|
includeLLB, err = json.Marshal(dir.Include)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2021-01-30 02:16:22 +01:00
|
|
|
}
|
2021-05-29 01:04:16 +02:00
|
|
|
excludeLLB := []byte("[]")
|
|
|
|
if len(dir.Exclude) > 0 {
|
|
|
|
var err error
|
|
|
|
excludeLLB, err = json.Marshal(dir.Exclude)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
2021-05-07 23:45:15 +02:00
|
|
|
|
|
|
|
p := dir.Path
|
|
|
|
if !filepath.IsAbs(p) {
|
2021-09-23 20:02:57 +02:00
|
|
|
p = filepath.Clean(path.Join(state.Project, dir.Path))
|
2021-05-19 04:15:17 +02:00
|
|
|
}
|
2021-09-23 20:02:57 +02:00
|
|
|
if !strings.HasPrefix(p, state.Project) {
|
|
|
|
return nil, fmt.Errorf("%q is outside the project", dir.Path)
|
2021-05-07 23:45:15 +02:00
|
|
|
}
|
|
|
|
|
2021-03-24 18:37:50 +01:00
|
|
|
llb := fmt.Sprintf(
|
2021-05-29 01:04:16 +02:00
|
|
|
`#up: [{do:"local",dir:"%s", include:%s, exclude:%s}]`,
|
2021-05-07 23:45:15 +02:00
|
|
|
p,
|
2021-03-24 18:37:50 +01:00
|
|
|
includeLLB,
|
2021-05-29 01:04:16 +02:00
|
|
|
excludeLLB,
|
2021-03-24 18:37:50 +01:00
|
|
|
)
|
|
|
|
return compiler.Compile("", llb)
|
|
|
|
}
|
|
|
|
|
|
|
|
// An input artifact loaded from a git repository
|
|
|
|
type gitInput struct {
|
2021-05-29 01:04:16 +02:00
|
|
|
Remote string `yaml:"remote,omitempty"`
|
|
|
|
Ref string `yaml:"ref,omitempty"`
|
|
|
|
Dir string `yaml:"dir,omitempty"`
|
2021-03-24 18:37:50 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func GitInput(remote, ref, dir string) Input {
|
2021-03-26 22:11:54 +01:00
|
|
|
return Input{
|
|
|
|
Git: &gitInput{
|
|
|
|
Remote: remote,
|
|
|
|
Ref: ref,
|
|
|
|
Dir: dir,
|
|
|
|
},
|
2021-01-30 02:16:22 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-26 03:56:16 +02:00
|
|
|
func (git gitInput) Compile(_ string, _ *State) (*compiler.Value, error) {
|
2021-04-02 01:45:58 +02:00
|
|
|
ref := "HEAD"
|
|
|
|
if git.Ref != "" {
|
|
|
|
ref = git.Ref
|
|
|
|
}
|
|
|
|
|
2021-08-17 15:06:28 +02:00
|
|
|
dir := ""
|
|
|
|
if git.Dir != "" {
|
|
|
|
dir = fmt.Sprintf(`,{do:"subdir", dir:"%s"}`, git.Dir)
|
|
|
|
}
|
|
|
|
|
2021-04-02 01:45:58 +02:00
|
|
|
return compiler.Compile("", fmt.Sprintf(
|
2021-08-17 15:06:28 +02:00
|
|
|
`#up: [{do:"fetch-git", remote:"%s", ref:"%s"}%s]`,
|
2021-04-02 01:45:58 +02:00
|
|
|
git.Remote,
|
|
|
|
ref,
|
2021-08-17 15:06:28 +02:00
|
|
|
dir,
|
2021-04-02 01:45:58 +02:00
|
|
|
))
|
2021-01-30 02:16:22 +01:00
|
|
|
}
|
|
|
|
|
2021-03-24 18:37:50 +01:00
|
|
|
// An input artifact loaded from a docker container
|
|
|
|
func DockerInput(ref string) Input {
|
2021-03-26 22:11:54 +01:00
|
|
|
return Input{
|
|
|
|
Docker: &dockerInput{
|
|
|
|
Ref: ref,
|
|
|
|
},
|
2021-01-30 02:16:22 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-24 18:37:50 +01:00
|
|
|
type dockerInput struct {
|
2021-05-29 01:04:16 +02:00
|
|
|
Ref string `yaml:"ref,omitempty"`
|
2021-01-30 02:16:22 +01:00
|
|
|
}
|
|
|
|
|
2021-05-26 03:56:16 +02:00
|
|
|
func (i dockerInput) Compile(_ string, _ *State) (*compiler.Value, error) {
|
2021-03-24 18:37:50 +01:00
|
|
|
panic("NOT IMPLEMENTED")
|
2021-01-30 02:16:22 +01:00
|
|
|
}
|
|
|
|
|
2021-03-24 18:37:50 +01:00
|
|
|
// An input value encoded as text
|
|
|
|
func TextInput(data string) Input {
|
2021-05-17 20:12:46 +02:00
|
|
|
i := textInput(data)
|
2021-03-26 22:11:54 +01:00
|
|
|
return Input{
|
2021-05-17 20:12:46 +02:00
|
|
|
Text: &i,
|
2021-01-30 02:16:22 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-17 20:12:46 +02:00
|
|
|
type textInput string
|
2021-01-30 02:16:22 +01:00
|
|
|
|
2021-05-26 03:56:16 +02:00
|
|
|
func (i textInput) Compile(_ string, _ *State) (*compiler.Value, error) {
|
2021-05-17 20:12:46 +02:00
|
|
|
return compiler.Compile("", fmt.Sprintf("%q", i))
|
|
|
|
}
|
|
|
|
|
|
|
|
// A secret input value
|
|
|
|
func SecretInput(data string) Input {
|
|
|
|
i := secretInput(data)
|
|
|
|
return Input{
|
|
|
|
Secret: &i,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type secretInput string
|
|
|
|
|
2021-09-16 17:30:15 +02:00
|
|
|
func (i secretInput) Compile(key string, _ *State) (*compiler.Value, error) {
|
2021-09-10 23:10:39 +02:00
|
|
|
hash := sha256.New()
|
|
|
|
hash.Write([]byte(key))
|
2021-09-16 17:30:15 +02:00
|
|
|
checksum := hash.Sum([]byte(i.PlainText()))
|
2021-09-10 23:10:39 +02:00
|
|
|
secretValue := fmt.Sprintf(`{id:"secret=%s;hash=%x"}`, key, checksum)
|
|
|
|
return compiler.Compile("", secretValue)
|
2021-05-26 03:56:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func (i secretInput) PlainText() string {
|
|
|
|
return string(i)
|
2021-01-30 02:16:22 +01:00
|
|
|
}
|
|
|
|
|
2021-08-24 16:00:53 +02:00
|
|
|
// An input value encoded as Bool
|
|
|
|
func BoolInput(data string) Input {
|
|
|
|
i := boolInput(data)
|
|
|
|
return Input{
|
|
|
|
Bool: &i,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type boolInput string
|
|
|
|
|
|
|
|
func (i boolInput) Compile(_ string, _ *State) (*compiler.Value, error) {
|
|
|
|
s := map[boolInput]struct{}{
|
|
|
|
"true": {},
|
|
|
|
"false": {},
|
|
|
|
}
|
|
|
|
if _, ok := s[i]; ok {
|
|
|
|
return compiler.DecodeJSON("", []byte(i))
|
|
|
|
}
|
|
|
|
return nil, fmt.Errorf("%q is not a valid boolean: <true|false>", i)
|
|
|
|
}
|
|
|
|
|
2021-03-24 18:37:50 +01:00
|
|
|
// An input value encoded as JSON
|
|
|
|
func JSONInput(data string) Input {
|
2021-05-17 20:12:46 +02:00
|
|
|
i := jsonInput(data)
|
2021-03-26 22:11:54 +01:00
|
|
|
return Input{
|
2021-05-17 20:12:46 +02:00
|
|
|
JSON: &i,
|
2021-01-30 02:16:22 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-17 20:12:46 +02:00
|
|
|
type jsonInput string
|
2021-01-30 02:16:22 +01:00
|
|
|
|
2021-05-26 03:56:16 +02:00
|
|
|
func (i jsonInput) Compile(_ string, _ *State) (*compiler.Value, error) {
|
2021-05-17 20:12:46 +02:00
|
|
|
return compiler.DecodeJSON("", []byte(i))
|
2021-01-30 02:16:22 +01:00
|
|
|
}
|
|
|
|
|
2021-03-24 18:37:50 +01:00
|
|
|
// An input value encoded as YAML
|
|
|
|
func YAMLInput(data string) Input {
|
2021-05-17 20:12:46 +02:00
|
|
|
i := yamlInput(data)
|
2021-03-26 22:11:54 +01:00
|
|
|
return Input{
|
2021-05-17 20:12:46 +02:00
|
|
|
YAML: &i,
|
2021-03-18 23:03:45 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-17 20:12:46 +02:00
|
|
|
type yamlInput string
|
2021-03-18 23:03:45 +01:00
|
|
|
|
2021-05-26 03:56:16 +02:00
|
|
|
func (i yamlInput) Compile(_ string, _ *State) (*compiler.Value, error) {
|
2021-05-17 20:12:46 +02:00
|
|
|
return compiler.DecodeYAML("", []byte(i))
|
2021-01-30 02:16:22 +01:00
|
|
|
}
|
2021-04-09 08:52:17 +02:00
|
|
|
|
|
|
|
func FileInput(data string) Input {
|
|
|
|
return Input{
|
|
|
|
File: &fileInput{
|
|
|
|
Path: data,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type fileInput struct {
|
|
|
|
Path string `json:"data,omitempty"`
|
|
|
|
}
|
|
|
|
|
2021-05-26 03:56:16 +02:00
|
|
|
func (i fileInput) Compile(_ string, _ *State) (*compiler.Value, error) {
|
2021-04-09 08:52:17 +02:00
|
|
|
data, err := ioutil.ReadFile(i.Path)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
value := compiler.NewValue()
|
|
|
|
if err := value.FillPath(cue.MakePath(), data); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return value, nil
|
|
|
|
}
|
2021-09-17 02:18:05 +02:00
|
|
|
|
|
|
|
// A socket input value
|
|
|
|
func SocketInput(data string) Input {
|
|
|
|
i := socketInput{
|
|
|
|
Unix: data,
|
|
|
|
}
|
|
|
|
return Input{
|
|
|
|
Socket: &i,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type socketInput struct {
|
|
|
|
Unix string `json:"unix,omitempty"`
|
|
|
|
}
|
|
|
|
|
|
|
|
func (i socketInput) Compile(_ string, _ *State) (*compiler.Value, error) {
|
|
|
|
socketValue := fmt.Sprintf(`{unix: %q}`, i.Unix)
|
|
|
|
return compiler.Compile("", socketValue)
|
|
|
|
}
|