blob: f6e15897139bffb9bdfa17d85134d4534297c394 [file] [log] [blame]
Abhay Kumara61c5222025-11-10 07:32:50 +00001// Copyright 2016 CoreOS, Inc.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package runtime
16
17import (
18 "fmt"
19 "os"
20 "strings"
21 "sync"
22)
23
24var (
25 ErrNoExist = fmt.Errorf("failpoint: failpoint does not exist")
26 ErrDisabled = fmt.Errorf("failpoint: failpoint is disabled")
27
28 failpoints map[string]*Failpoint
29 // failpointsMu protects the failpoints map, preventing concurrent
30 // accesses during commands such as Enabling and Disabling
31 failpointsMu sync.RWMutex
32
33 envTerms map[string]string
34
35 // panicMu (panic mutex) ensures that the action of panic failpoints
36 // and serving of the HTTP requests won't be executed at the same time,
37 // avoiding the possibility that the server runtime panics during processing
38 // requests
39 panicMu sync.Mutex
40)
41
42func init() {
43 failpoints = make(map[string]*Failpoint)
44 envTerms = make(map[string]string)
45 if s := os.Getenv("GOFAIL_FAILPOINTS"); len(s) > 0 {
46 fpMap, err := parseFailpoints(s)
47 if err != nil {
48 fmt.Printf("fail to parse failpoint: %v\n", err)
49 os.Exit(1)
50 }
51 envTerms = fpMap
52 }
53 if s := os.Getenv("GOFAIL_HTTP"); len(s) > 0 {
54 if err := serve(s); err != nil {
55 fmt.Println(err)
56 os.Exit(1)
57 }
58 }
59}
60
61func parseFailpoints(fps string) (map[string]string, error) {
62 // The format is <FAILPOINT>=<TERMS>[;<FAILPOINT>=<TERMS>]*
63 fpMap := map[string]string{}
64
65 for _, fp := range strings.Split(fps, ";") {
66 if len(fp) == 0 {
67 continue
68 }
69 fpTerm := strings.Split(fp, "=")
70 if len(fpTerm) != 2 {
71 err := fmt.Errorf("bad failpoint %q", fp)
72 return nil, err
73 }
74 fpMap[fpTerm[0]] = fpTerm[1]
75 }
76 return fpMap, nil
77}
78
79// Enable sets a failpoint to a given failpoint description.
80func Enable(name, inTerms string) error {
81 failpointsMu.RLock()
82 fp := failpoints[name]
83 failpointsMu.RUnlock()
84 if fp == nil {
85 return ErrNoExist
86 }
87
88 t, err := newTerms(name, inTerms)
89 if err != nil {
90 fmt.Printf("failed to enable \"%s=%s\" (%v)\n", name, inTerms, err)
91 return err
92 }
93
94 fp.SetTerm(t)
95
96 return nil
97}
98
99// Disable stops a failpoint from firing.
100func Disable(name string) error {
101 failpointsMu.RLock()
102 fp := failpoints[name]
103 failpointsMu.RUnlock()
104 if fp == nil {
105 return ErrNoExist
106 }
107
108 return fp.ClearTerm()
109}
110
111// Status gives the current setting and execution count for the failpoint
112func Status(failpath string) (string, int, error) {
113 failpointsMu.RLock()
114 fp := failpoints[failpath]
115 failpointsMu.RUnlock()
116 if fp == nil {
117 return "", 0, ErrNoExist
118 }
119
120 return fp.Status()
121}
122
123func List() []string {
124 failpointsMu.Lock()
125 defer failpointsMu.Unlock()
126 return list()
127}
128
129func list() []string {
130 ret := make([]string, 0, len(failpoints))
131 for fp := range failpoints {
132 ret = append(ret, fp)
133 }
134 return ret
135}
136
137func register(name string) *Failpoint {
138 failpointsMu.Lock()
139 if _, ok := failpoints[name]; ok {
140 failpointsMu.Unlock()
141 panic(fmt.Sprintf("failpoint name %s is already registered.", name))
142 }
143
144 fp := &Failpoint{}
145 failpoints[name] = fp
146 failpointsMu.Unlock()
147 if t, ok := envTerms[name]; ok {
148 Enable(name, t)
149 }
150 return fp
151}