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 #### files
Allows the addition of user specified 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. Note that the file contents are not required to be base64 encoded.
Type: `array` Type: `array`
@ -249,11 +253,12 @@ Type: `array`
Examples: Examples:
```yaml ```yaml
kubelet: files:
contents: | - content: |
... ...
permissions: 0666 permissions: 0666
path: /tmp/file.txt path: /tmp/file.txt
op: append
``` ```

View File

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

View File

@ -31,12 +31,30 @@ func (task *ExtraFiles) TaskFunc(mode runtime.Mode) phase.TaskFunc {
return task.runtime return task.runtime
} }
// nolint: gocyclo
func (task *ExtraFiles) runtime(r runtime.Runtime) (err error) { func (task *ExtraFiles) runtime(r runtime.Runtime) (err error) {
var result *multierror.Error var result *multierror.Error
for _, f := range r.Config().Machine().Files() { for _, f := range r.Config().Machine().Files() {
// Slurp existing file if append is our op and add contents to it content := f.Content
if f.Op == "append" {
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 var existingFileContents []byte
existingFileContents, err = ioutil.ReadFile(f.Path) existingFileContents, err = ioutil.ReadFile(f.Path)
@ -45,7 +63,10 @@ func (task *ExtraFiles) runtime(r runtime.Runtime) (err error) {
continue 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. // Determine if supplied path is in /var or not.
@ -62,12 +83,18 @@ func (task *ExtraFiles) runtime(r runtime.Runtime) (err error) {
inVar = false 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 { if err = os.MkdirAll(filepath.Dir(p), os.ModeDir); err != nil {
result = multierror.Append(result, err) result = multierror.Append(result, err)
continue 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) result = multierror.Append(result, err)
continue continue
} }
@ -82,3 +109,35 @@ func (task *ExtraFiles) runtime(r runtime.Runtime) (err error) {
return result.ErrorOrNil() 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. // File represents a file to write to disk.
type File struct { type File struct {
Contents string `yaml:"contents"` Content string `yaml:"content"`
Permissions os.FileMode `yaml:"permissions"` Permissions os.FileMode `yaml:"permissions"`
Path string `yaml:"path"` Path string `yaml:"path"`
Op string `yaml:"op"` Op string `yaml:"op"`

View File

@ -125,14 +125,19 @@ type MachineConfig struct {
MachineInstall *InstallConfig `yaml:"install,omitempty"` MachineInstall *InstallConfig `yaml:"install,omitempty"`
// description: | // description: |
// Allows the addition of user specified 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. // Note that the file contents are not required to be base64 encoded.
// examples: // examples:
// - | // - |
// kubelet: // files:
// contents: | // - content: |
// ... // ...
// permissions: 0666 // permissions: 0666
// path: /tmp/file.txt // path: /tmp/file.txt
// op: append
MachineFiles []machine.File `yaml:"files,omitempty"` // Note: The specified `path` is relative to `/var`. MachineFiles []machine.File `yaml:"files,omitempty"` // Note: The specified `path` is relative to `/var`.
// description: | // description: |
// The `env` field allows for the addition of environment variables to a machine. // The `env` field allows for the addition of environment variables to a machine.