1
// Copyright 2014 Canonical Ltd.
2
// Licensed under the AGPLv3, see LICENCE file for details.
14
"github.com/juju/loggo"
15
"github.com/juju/utils/series"
17
"github.com/juju/juju/juju/paths"
24
dataDir = paths.MustSucceed(paths.DataDir(series.HostSeries()))
25
wrenchDir = filepath.Join(dataDir, "wrench")
29
var logger = loggo.GetLogger("juju.wrench")
31
// IsActive returns true if a "wrench" of a certain category and
32
// feature should be "dropped in the works".
34
// This function may be called at specific points in the Juju codebase
35
// to introduce otherwise hard to induce failure modes for the
36
// purposes of manual or CI testing. The "<juju_datadir>/wrench/"
37
// directory will be checked for "wrench files" which this function
40
// Wrench files are line-based, with each line indicating some
41
// (mis-)feature to enable for a given part of the code. The should be
42
// created on the host where the fault should be triggered.
44
// For example, /var/lib/juju/wrench/machine-agent could contain:
47
// fail-api-server-start
49
// The caller need not worry about errors. Any errors that occur will
50
// be logged and false will be returned.
51
func IsActive(category, feature string) bool {
55
if !checkWrenchDir(wrenchDir) {
58
fileName := filepath.Join(wrenchDir, category)
59
if !checkWrenchFile(category, feature, fileName) {
63
wrenchFile, err := os.Open(fileName)
65
logger.Errorf("unable to read wrench data for %s/%s (ignored): %v",
66
category, feature, err)
69
defer wrenchFile.Close()
70
lines := bufio.NewScanner(wrenchFile)
72
line := strings.TrimSpace(lines.Text())
74
logger.Debugf("wrench for %s/%s is active", category, feature)
78
if err := lines.Err(); err != nil {
79
logger.Errorf("error while reading wrench data for %s/%s (ignored): %v",
80
category, feature, err)
85
// SetEnabled turns the wrench feature on or off globally.
87
// If false is given, all future IsActive calls will unconditionally
88
// return false. If true is given, all future IsActive calls will
89
// return true for active wrenches.
91
// The previous value for the global wrench enable flag is returned.
92
func SetEnabled(next bool) bool {
94
defer enabledMu.Unlock()
100
// IsEnabled returns true if the wrench feature is turned on globally.
101
func IsEnabled() bool {
103
defer enabledMu.Unlock()
107
var stat = os.Stat // To support patching
109
func checkWrenchDir(dirName string) bool {
110
dirinfo, err := stat(dirName)
112
logger.Debugf("couldn't read wrench directory: %v", err)
115
if !isOwnedByJujuUser(dirinfo) {
116
logger.Errorf("wrench directory has incorrect ownership - wrench "+
117
"functionality disabled (%s)", wrenchDir)
123
func checkWrenchFile(category, feature, fileName string) bool {
124
fileinfo, err := stat(fileName)
126
logger.Debugf("no wrench data for %s/%s (ignored): %v",
127
category, feature, err)
130
if !isOwnedByJujuUser(fileinfo) {
131
logger.Errorf("wrench file for %s/%s has incorrect ownership "+
132
"- ignoring %s", category, feature, fileName)
135
// Windows is not fully POSIX compliant
136
if runtime.GOOS != "windows" {
137
if fileinfo.Mode()&0022 != 0 {
138
logger.Errorf("wrench file for %s/%s should only be writable by "+
139
"owner - ignoring %s", category, feature, fileName)