diff --git a/notify.go b/notify.go new file mode 100644 index 0000000..2b91916 --- /dev/null +++ b/notify.go @@ -0,0 +1,43 @@ +/* + * Copyright 2023 Red Hat, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package podfingerprint + +var completionSink chan<- Status + +// SetCompletionSink sets the process-global completion destination +func SetCompletionSink(ch chan<- Status) { + completionSink = ch +} + +// MarkCompleted sets a podfingerprint Status as completed. +// When a Status is completed, it means the Status and its fingerprint is considered sealed, +// so from now on only idempotent, non-state altering actions (like Repr(), Sign(), Check()) +// are expected to be performed. Applications which use the Tracing fingerprint can use this +// function to clearly signal this completion point and as another option to pass the Status +// to other subssystems. +func MarkCompleted(st Status) error { + if completionSink == nil { // nothing to do + return nil + } + completionSink <- st.Clone() + return nil +} + +// CleanCompletionSink should be used only in testing +func CleanCompletionSink() { + completionSink = nil +} diff --git a/notify_test.go b/notify_test.go new file mode 100644 index 0000000..5b9d6ed --- /dev/null +++ b/notify_test.go @@ -0,0 +1,92 @@ +/* + * Copyright 2023 Red Hat, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package podfingerprint + +import ( + "testing" +) + +func TestMarkCompleted(t *testing.T) { + type testCase struct { + name string + sink chan Status + status Status + expectedError error + } + + testCases := []testCase{ + { + name: "no completion sink", + status: Status{ + NodeName: "test-node-0", + Pods: []NamespacedName{ + { + Namespace: "ns-1", + Name: "pod-1", + }, + { + Namespace: "ns-2", + Name: "pod-2", + }, + { + Namespace: "ns-2", + Name: "pod-3", + }, + }, + FingerprintExpected: "pfp0v001807d932586d44a8a", + FingerprintComputed: "pfp0v001807d932586d44a8a", + }, + sink: nil, // explicit + }, + { + name: "with completion sink", + status: Status{ + NodeName: "test-node-0", + Pods: []NamespacedName{ + { + Namespace: "ns-1", + Name: "pod-1", + }, + { + Namespace: "ns-2", + Name: "pod-2", + }, + { + Namespace: "ns-2", + Name: "pod-3", + }, + }, + FingerprintExpected: "pfp0v001807d932586d44a8a", + FingerprintComputed: "pfp0v001807d932586d44a8a", + }, + sink: make(chan Status, 5), // anything > 1 is fine + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + SetCompletionSink(tc.sink) + err := MarkCompleted(tc.status) + if err != tc.expectedError { + t.Errorf("got error=%v expected=%v", err, tc.expectedError) + } + if tc.sink != nil && len(tc.sink) != 1 { + t.Errorf("unexpected data in sink: %d", len(tc.sink)) + } + }) + } +} diff --git a/tools/pfp/main.go b/tools/pfp/main.go index 390da86..d7a21af 100644 --- a/tools/pfp/main.go +++ b/tools/pfp/main.go @@ -40,7 +40,7 @@ func main() { flag.Parse() var fp Fingerprinter - var st podfingerprint.Status + st := podfingerprint.MakeStatus("STDIN") if *withTrace { fp = podfingerprint.NewTracingFingerprint(0, &st) } else { @@ -59,7 +59,11 @@ func main() { } fp.Add(fields[0], fields[1]) } - fmt.Println(fp.Sign()) + pfp := fp.Sign() + if *withTrace { + podfingerprint.MarkCompleted(st) + } + fmt.Println(pfp) if *withTrace { json.NewEncoder(os.Stderr).Encode(st)