1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
|
// Copyright (c) 2019, Sylabs Inc. All rights reserved.
// This software is licensed under a 3-clause BSD license. Please consult the
// LICENSE.md file distributed with the sources of this project regarding your
// rights to use or distribute this software.
package singularity
import (
"bytes"
"fmt"
"io"
"os"
"strings"
"github.com/sylabs/singularity/v4/pkg/util/singularityconf"
"golang.org/x/sys/unix"
)
// GlobalConfigOp defines a type for a global configuration operation.
type GlobalConfigOp uint8
const (
// GlobalConfigSet is the operation to set a configuration directive value.
GlobalConfigSet GlobalConfigOp = iota
// GlobalConfigUnset is the operation to unset a configuration directive value.
GlobalConfigUnset
// GlobalConfigGet is the operation to get a configuration directive value.
GlobalConfigGet
// GlobalConfigReset is the operation to reset a configuration directive value.
GlobalConfigReset
)
func contains(slice []string, val string) bool {
for _, s := range slice {
if s == val {
return true
}
}
return false
}
func generateConfig(path string, directives singularityconf.Directives, dry bool) error {
// Generate the config structure from our directives
c, err := singularityconf.GetConfig(directives)
if err != nil {
return fmt.Errorf("configuration directive invalid: %w", err)
}
// Write a config file to our in memory buffer
newConfig := new(bytes.Buffer)
if err := singularityconf.Generate(newConfig, "", c); err != nil {
return fmt.Errorf("while generating configuration from template: %w", err)
}
// Dry run = write to Stdout
out := os.Stdout
// Not dry run = create / overwrite existing file, now we know we have valid content
if !dry {
unix.Umask(0)
flags := os.O_CREATE | os.O_TRUNC | unix.O_NOFOLLOW | os.O_RDWR
nf, err := os.OpenFile(path, flags, 0o644)
if err != nil {
return fmt.Errorf("while creating configuration file %s: %w", path, err)
}
defer nf.Close()
out = nf
}
_, err = io.Copy(out, newConfig)
if err != nil {
return fmt.Errorf("while writing configuration file %s: %w", path, err)
}
return nil
}
// GlobalConfig allows to set/unset/reset a configuration directive value
// in singularity.conf
func GlobalConfig(args []string, configFile string, dry bool, op GlobalConfigOp) error {
directive := args[0]
value := ""
if directive == "" {
return fmt.Errorf("you must specify a configuration directive")
}
if len(args) > 1 {
value = args[1]
}
if !singularityconf.HasDirective(directive) {
return fmt.Errorf("%q is not a valid configuration directive", directive)
}
f, err := os.OpenFile(configFile, os.O_RDONLY, 0o644)
if err != nil {
return fmt.Errorf("while opening configuration file %s: %s", configFile, err)
}
defer f.Close()
directives, err := singularityconf.GetDirectives(f)
if err != nil {
return err
}
values := []string{}
if value != "" {
for _, v := range strings.Split(value, ",") {
va := strings.TrimSpace(v)
if va != "" {
if contains(values, va) {
continue
}
values = append(values, va)
}
}
}
switch op {
case GlobalConfigSet:
if len(values) == 0 {
return fmt.Errorf("you must specify a value for directive %q", directive)
}
if _, ok := directives[directive]; ok {
for i := len(values) - 1; i >= 0; i-- {
if contains(directives[directive], values[i]) {
values = append(values[:i], values[i+1:]...)
}
}
directives[directive] = append(values, directives[directive]...)
} else {
directives[directive] = values
}
case GlobalConfigUnset:
unset := false
for i := len(directives[directive]) - 1; i >= 0; i-- {
for _, v := range values {
if directives[directive][i] == v {
unset = true
directives[directive] = append(directives[directive][:i], directives[directive][i+1:]...)
}
}
}
if !unset {
return fmt.Errorf("value '%s' not found for directive %q", value, directive)
}
case GlobalConfigGet:
if len(directives[directive]) > 0 {
fmt.Println(strings.Join(directives[directive], ","))
}
return nil
case GlobalConfigReset:
delete(directives, directive)
}
return generateConfig(configFile, directives, dry)
}
|