feat: add create and overwrite file operations

This adds `create` and `overwrite` file operations so that we can have
better sanity checks around extra files.

Signed-off-by: Andrew Rynhard <andrew@andrewrynhard.com>
This commit is contained in:
Andrew Rynhard 2019-12-05 22:34:43 +00:00
parent ad863a7f92
commit fa4fb4d444
5 changed files with 85 additions and 16 deletions

View File

@ -242,6 +242,10 @@ install:
#### files
Allows the addition of user specified files.
The value of `op` can be `create`, `overwrite`, or `append`.
In the case of `create`, `path` must not exist.
In the case of `overwrite`, and `append`, `path` must be a valid file.
If an `op` value of `append` is used, the existing file will be appended.
Note that the file contents are not required to be base64 encoded.
Type: `array`
@ -249,11 +253,12 @@ Type: `array`
Examples:
```yaml
kubelet:
contents: |
...
permissions: 0666
path: /tmp/file.txt
files:
- content: |
...
permissions: 0666
path: /tmp/file.txt
op: append
```

View File

@ -10,7 +10,7 @@ Put into each machine the PEM encoded certificate:
machine:
...
files:
- contents: |
- content: |
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----

View File

@ -31,12 +31,30 @@ func (task *ExtraFiles) TaskFunc(mode runtime.Mode) phase.TaskFunc {
return task.runtime
}
// nolint: gocyclo
func (task *ExtraFiles) runtime(r runtime.Runtime) (err error) {
var result *multierror.Error
for _, f := range r.Config().Machine().Files() {
// Slurp existing file if append is our op and add contents to it
if f.Op == "append" {
content := f.Content
switch f.Op {
case "create":
if err = doesNotExists(f.Path); err != nil {
result = multierror.Append(result, fmt.Errorf("file must not exist: %q", f.Path))
continue
}
case "overwrite":
if err = existsAndIsFile(f.Path); err != nil {
result = multierror.Append(result, err)
continue
}
case "append":
if err = existsAndIsFile(f.Path); err != nil {
result = multierror.Append(result, err)
continue
}
var existingFileContents []byte
existingFileContents, err = ioutil.ReadFile(f.Path)
@ -45,7 +63,10 @@ func (task *ExtraFiles) runtime(r runtime.Runtime) (err error) {
continue
}
f.Contents = string(existingFileContents) + "\n" + f.Contents
content = string(existingFileContents) + "\n" + f.Content
default:
result = multierror.Append(result, fmt.Errorf("unknown operation for file %q: %q", f.Path, f.Op))
continue
}
// Determine if supplied path is in /var or not.
@ -62,12 +83,18 @@ func (task *ExtraFiles) runtime(r runtime.Runtime) (err error) {
inVar = false
}
// We do not want to support creating new files anywhere outside of
// /var. If a valid use case comes up, we can reconsider then.
if !inVar && f.Op == "create" {
return fmt.Errorf("create operation not allowed outside of /var: %q", f.Path)
}
if err = os.MkdirAll(filepath.Dir(p), os.ModeDir); err != nil {
result = multierror.Append(result, err)
continue
}
if err = ioutil.WriteFile(p, []byte(f.Contents), f.Permissions); err != nil {
if err = ioutil.WriteFile(p, []byte(content), f.Permissions); err != nil {
result = multierror.Append(result, err)
continue
}
@ -82,3 +109,35 @@ func (task *ExtraFiles) runtime(r runtime.Runtime) (err error) {
return result.ErrorOrNil()
}
func doesNotExists(p string) (err error) {
_, err = os.Stat(p)
if err != nil {
if os.IsNotExist(err) {
return nil
}
return err
}
return fmt.Errorf("file exists")
}
func existsAndIsFile(p string) (err error) {
var info os.FileInfo
info, err = os.Stat(p)
if err != nil {
if !os.IsNotExist(err) {
return err
}
return fmt.Errorf("file must exist: %q", p)
}
if !info.Mode().IsRegular() {
return fmt.Errorf("invalid mode: %q", info.Mode().String())
}
return nil
}

View File

@ -44,7 +44,7 @@ type Env = map[string]string
// File represents a file to write to disk.
type File struct {
Contents string `yaml:"contents"`
Content string `yaml:"content"`
Permissions os.FileMode `yaml:"permissions"`
Path string `yaml:"path"`
Op string `yaml:"op"`

View File

@ -125,14 +125,19 @@ type MachineConfig struct {
MachineInstall *InstallConfig `yaml:"install,omitempty"`
// description: |
// Allows the addition of user specified files.
// The value of `op` can be `create`, `overwrite`, or `append`.
// In the case of `create`, `path` must not exist.
// In the case of `overwrite`, and `append`, `path` must be a valid file.
// If an `op` value of `append` is used, the existing file will be appended.
// Note that the file contents are not required to be base64 encoded.
// examples:
// - |
// kubelet:
// contents: |
// ...
// permissions: 0666
// path: /tmp/file.txt
// files:
// - content: |
// ...
// permissions: 0666
// path: /tmp/file.txt
// op: append
MachineFiles []machine.File `yaml:"files,omitempty"` // Note: The specified `path` is relative to `/var`.
// description: |
// The `env` field allows for the addition of environment variables to a machine.