Skip to content

Commit 4a0b5dc

Browse files
committed
collect metrics for the agent lockfile (if present)
if the agent is currently disabled (`puppet agent --disable`), additional metrics are being reported. currently only the start of the lock is repored (the modification time of the agent lockfile) Signed-off-by: Gordon Bleux <[email protected]>
1 parent 4cf299b commit 4a0b5dc

File tree

17 files changed

+322
-21
lines changed

17 files changed

+322
-21
lines changed

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ puppet_last_run_duration_seconds 28.023470087
2929
# HELP puppet_last_run_success 1 if the last Puppet run was successful.
3030
# TYPE puppet_last_run_success gauge
3131
puppet_last_run_success 1
32+
# HELP puppet_disabled_since_seconds Unix timestamp since when puppet has been disabled.
33+
# TYPE puppet_disabled_since_seconds gauge
34+
puppet_disabled_since_seconds 1.6863428664914448e+09
3235
```
3336

3437
### Example Alert Rules

internal/logging/logger.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Copyright 2023 RetailNext, 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+
15+
package logging
16+
17+
// Logger is a vendor-agnostic interface for event tracking
18+
type Logger interface {
19+
Errorw(msg string, keysAndValues ...interface{})
20+
Panicw(msg string, keysAndValues ...interface{})
21+
}

internal/logging/noop.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// Copyright 2023 RetailNext, 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+
15+
package logging
16+
17+
type noOpLogger struct{}
18+
19+
// NewNoOpLogger returns a Logger implementation with no side effects
20+
func NewNoOpLogger() Logger { return noOpLogger{} }
21+
22+
func (noOpLogger) Errorw(_msg string, _keysAndValues ...interface{}) {}
23+
24+
func (noOpLogger) Panicw(_msg string, _keysAndValues ...interface{}) {}

internal/logging/noop_test.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// Copyright 2023 RetailNext, 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+
15+
package logging
16+
17+
import (
18+
"testing"
19+
)
20+
21+
func TestNoOpLogger(t *testing.T) {
22+
got := NewNoOpLogger()
23+
24+
got.Errorw("error", "key", "value")
25+
got.Panicw("panic", "key", "value")
26+
got.Errorw("error", "key")
27+
got.Panicw("panic", "key")
28+
got.Errorw("error")
29+
got.Panicw("panic")
30+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
// Copyright 2023 RetailNext, 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+
15+
package puppet
16+
17+
import (
18+
"encoding/json"
19+
"io"
20+
"os"
21+
"time"
22+
)
23+
24+
// AgentDisabledLockfile holds metadata when the
25+
// agent has been administratively disabled.
26+
type AgentDisabledLockfile struct {
27+
DisabledMessage string `json:"disabled_message"`
28+
DisabledSince time.Time `json:-`
29+
}
30+
31+
// ParseAgentDisabledLockfile reads the file and its filesystem metadata
32+
// at the given path. The returned error can be the result of a filesystem
33+
// operation (including `os.ErrNotExist`) or parsing procedure.
34+
func ParseAgentDisabledLockfile(path string) (*AgentDisabledLockfile, error) {
35+
info, err := os.Stat(path)
36+
if err != nil {
37+
return nil, err
38+
}
39+
40+
file, err := os.Open(path)
41+
if err != nil {
42+
return nil, err
43+
}
44+
defer file.Close()
45+
46+
data, err := io.ReadAll(file)
47+
if err != nil {
48+
return nil, err
49+
}
50+
51+
result := &AgentDisabledLockfile{
52+
DisabledSince: info.ModTime(),
53+
}
54+
55+
err = json.Unmarshal(data, result)
56+
if err != nil {
57+
return nil, err
58+
}
59+
60+
return result, nil
61+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// Copyright 2023 RetailNext, 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+
15+
package puppet
16+
17+
import (
18+
"testing"
19+
)
20+
21+
func TestParseAgentDisabledLockfile(t *testing.T) {
22+
testCases := map[string]struct {
23+
wantDisabledMessage string
24+
wantError bool
25+
}{
26+
"404": {wantError: true},
27+
"empty": {wantError: true},
28+
"malformed": {wantError: true},
29+
"empty_hash": {},
30+
"disabled_message": {wantDisabledMessage: "testing unmarshalling"},
31+
}
32+
33+
for name, tc := range testCases {
34+
t.Run(name, func(t *testing.T) {
35+
got, err := ParseAgentDisabledLockfile("testdata/agent_disabled_lockfile/" + name + ".json")
36+
37+
if tc.wantError {
38+
if err == nil {
39+
t.Errorf("expected fixture %q to produce error but got none", name)
40+
}
41+
} else {
42+
if err != nil {
43+
t.Errorf("expected fixture %q to parse properly but got error\n%s", name, err)
44+
}
45+
if got == nil {
46+
t.Errorf("expected fixture %q to parse properly but got nil as result", name)
47+
}
48+
}
49+
})
50+
}
51+
}

internal/puppet/defaults.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Copyright 2023 RetailNext, 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+
15+
package puppet
16+
17+
const (
18+
DefaultConfigFile = "/etc/puppetlabs/puppet/puppet.conf"
19+
DefaultAgentDisabledLockfile = "/opt/puppetlabs/puppet/cache/state/agent_disabled.lock"
20+
DefaultLastRunReportFile = "/opt/puppetlabs/puppet/cache/state/last_run_report.yaml"
21+
)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"disabled_message":"testing unmarshalling"}

internal/puppet/testdata/agent_disabled_lockfile/empty.json

Whitespace-only changes.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{}

0 commit comments

Comments
 (0)