github.com/chenbh/concourse/v6@v6.4.2/atc/task.go (about)

     1  package atc
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"fmt"
     7  	"strings"
     8  
     9  	"sigs.k8s.io/yaml"
    10  )
    11  
    12  type TaskConfig struct {
    13  	// The platform the task must run on (e.g. linux, windows).
    14  	Platform string `json:"platform,omitempty"`
    15  
    16  	// Optional string specifying an image to use for the build. Depending on the
    17  	// platform, this may or may not be required (e.g. Windows/OS X vs. Linux).
    18  	RootfsURI string `json:"rootfs_uri,omitempty"`
    19  
    20  	ImageResource *ImageResource `json:"image_resource,omitempty"`
    21  
    22  	// Limits to set on the Task Container
    23  	Limits *ContainerLimits `json:"container_limits,omitempty"`
    24  
    25  	// Parameters to pass to the task via environment variables.
    26  	Params TaskEnv `json:"params,omitempty"`
    27  
    28  	// Script to execute.
    29  	Run TaskRunConfig `json:"run,omitempty"`
    30  
    31  	// The set of (logical, name-only) inputs required by the task.
    32  	Inputs []TaskInputConfig `json:"inputs,omitempty"`
    33  
    34  	// The set of (logical, name-only) outputs provided by the task.
    35  	Outputs []TaskOutputConfig `json:"outputs,omitempty"`
    36  
    37  	// Path to cached directory that will be shared between builds for the same task.
    38  	Caches []TaskCacheConfig `json:"caches,omitempty"`
    39  }
    40  
    41  type ContainerLimits struct {
    42  	CPU    *uint64 `json:"cpu,omitempty"`
    43  	Memory *uint64 `json:"memory,omitempty"`
    44  }
    45  
    46  type ImageResource struct {
    47  	Type   string `json:"type"`
    48  	Source Source `json:"source"`
    49  
    50  	Params  Params  `json:"params,omitempty"`
    51  	Version Version `json:"version,omitempty"`
    52  }
    53  
    54  func NewTaskConfig(configBytes []byte) (TaskConfig, error) {
    55  	var config TaskConfig
    56  	err := yaml.UnmarshalStrict(configBytes, &config, yaml.DisallowUnknownFields)
    57  	if err != nil {
    58  		return TaskConfig{}, err
    59  	}
    60  
    61  	err = config.Validate()
    62  	if err != nil {
    63  		return TaskConfig{}, err
    64  	}
    65  
    66  	return config, nil
    67  }
    68  
    69  type TaskValidationError struct {
    70  	Errors []string
    71  }
    72  
    73  func (err TaskValidationError) Error() string {
    74  	return fmt.Sprintf("invalid task configuration:\n%s", strings.Join(err.Errors, "\n"))
    75  }
    76  
    77  func (config TaskConfig) Validate() error {
    78  	var errors []string
    79  
    80  	if config.Platform == "" {
    81  		errors = append(errors, "missing 'platform'")
    82  	}
    83  
    84  	if config.Run.Path == "" {
    85  		errors = append(errors, "missing path to executable to run")
    86  	}
    87  
    88  	errors = append(errors, config.validateInputContainsNames()...)
    89  	errors = append(errors, config.validateOutputContainsNames()...)
    90  
    91  	if len(errors) > 0 {
    92  		return TaskValidationError{
    93  			Errors: errors,
    94  		}
    95  	}
    96  
    97  	return nil
    98  }
    99  
   100  func (config TaskConfig) validateOutputContainsNames() []string {
   101  	var messages []string
   102  
   103  	for i, output := range config.Outputs {
   104  		if output.Name == "" {
   105  			messages = append(messages, fmt.Sprintf("  output in position %d is missing a name", i))
   106  		}
   107  	}
   108  
   109  	return messages
   110  }
   111  
   112  func (config TaskConfig) validateInputContainsNames() []string {
   113  	messages := []string{}
   114  
   115  	for i, input := range config.Inputs {
   116  		if input.Name == "" {
   117  			messages = append(messages, fmt.Sprintf("  input in position %d is missing a name", i))
   118  		}
   119  	}
   120  
   121  	return messages
   122  }
   123  
   124  type TaskRunConfig struct {
   125  	Path string   `json:"path"`
   126  	Args []string `json:"args,omitempty"`
   127  	Dir  string   `json:"dir,omitempty"`
   128  
   129  	// The user that the task will run as (defaults to whatever the docker image specifies)
   130  	User string `json:"user,omitempty"`
   131  }
   132  
   133  type TaskInputConfig struct {
   134  	Name     string `json:"name"`
   135  	Path     string `json:"path,omitempty"`
   136  	Optional bool   `json:"optional,omitempty"`
   137  }
   138  
   139  type TaskOutputConfig struct {
   140  	Name string `json:"name"`
   141  	Path string `json:"path,omitempty"`
   142  }
   143  
   144  type TaskCacheConfig struct {
   145  	Path string `json:"path,omitempty"`
   146  }
   147  
   148  type TaskEnv map[string]string
   149  
   150  func (te *TaskEnv) UnmarshalJSON(p []byte) error {
   151  	raw := map[string]CoercedString{}
   152  	err := json.Unmarshal(p, &raw)
   153  	if err != nil {
   154  		return err
   155  	}
   156  
   157  	m := map[string]string{}
   158  	for k, v := range raw {
   159  		m[k] = string(v)
   160  	}
   161  
   162  	*te = m
   163  
   164  	return nil
   165  }
   166  
   167  func (te TaskEnv) Env() []string {
   168  	env := make([]string, 0, len(te))
   169  
   170  	for k, v := range te {
   171  		env = append(env, k+"="+v)
   172  	}
   173  
   174  	return env
   175  }
   176  
   177  type CoercedString string
   178  
   179  func (cs *CoercedString) UnmarshalJSON(p []byte) error {
   180  	var raw interface{}
   181  	dec := json.NewDecoder(bytes.NewReader(p))
   182  	dec.UseNumber()
   183  	err := dec.Decode(&raw)
   184  	if err != nil {
   185  		return err
   186  	}
   187  
   188  	if raw == nil {
   189  		*cs = CoercedString("")
   190  		return nil
   191  	}
   192  	switch v := raw.(type) {
   193  	case string:
   194  		*cs = CoercedString(v)
   195  
   196  	case json.Number:
   197  		*cs = CoercedString(v)
   198  
   199  	default:
   200  		j, err := json.Marshal(v)
   201  		if err != nil {
   202  			return err
   203  		}
   204  
   205  		*cs = CoercedString(j)
   206  	}
   207  
   208  	return nil
   209  }