This repository has been archived by the owner on Apr 29, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 51
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
An audit log record will be created whenever nodes are scheduled or unscheduled by an RC. This will allow constructing a history of which nodes were managed by an RC or pod cluster over time.
- Loading branch information
Showing
7 changed files
with
346 additions
and
37 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
package rc | ||
|
||
import ( | ||
"context" | ||
|
||
"github.com/square/p2/pkg/audit" | ||
pc_fields "github.com/square/p2/pkg/pc/fields" | ||
"github.com/square/p2/pkg/store/consul/transaction" | ||
"github.com/square/p2/pkg/types" | ||
"github.com/square/p2/pkg/util" | ||
) | ||
|
||
type auditingTransaction struct { | ||
ctx context.Context | ||
nodes map[types.NodeName]struct{} | ||
auditLogStore AuditLogStore | ||
podID types.PodID | ||
az pc_fields.AvailabilityZone | ||
cn pc_fields.ClusterName | ||
} | ||
|
||
type scheduledNodesKey struct{} | ||
|
||
func (rc *replicationController) newAuditingTransaction( | ||
ctx context.Context, | ||
startingNodes []types.NodeName, | ||
) (*auditingTransaction, func()) { | ||
annotatedContext := context.WithValue(ctx, scheduledNodesKey{}, startingNodes) | ||
ctx, cancelFunc := transaction.New(annotatedContext) | ||
|
||
startingNodeMap := make(map[types.NodeName]struct{}) | ||
for _, node := range startingNodes { | ||
startingNodeMap[node] = struct{}{} | ||
} | ||
|
||
rc.mu.Lock() | ||
manifest := rc.Manifest | ||
podLabels := rc.PodLabels | ||
rc.mu.Unlock() | ||
return &auditingTransaction{ | ||
ctx: ctx, | ||
nodes: startingNodeMap, | ||
auditLogStore: rc.auditLogStore, | ||
podID: manifest.ID(), | ||
az: pc_fields.AvailabilityZone(podLabels[types.AvailabilityZoneLabel]), | ||
cn: pc_fields.ClusterName(podLabels[types.ClusterNameLabel]), | ||
}, cancelFunc | ||
} | ||
|
||
func (a *auditingTransaction) Context() context.Context { | ||
return a.ctx | ||
} | ||
|
||
func (a *auditingTransaction) Nodes() []types.NodeName { | ||
var nodes []types.NodeName | ||
for node, _ := range a.nodes { | ||
nodes = append(nodes, node) | ||
} | ||
return nodes | ||
} | ||
|
||
func (a *auditingTransaction) AddNode(node types.NodeName) { | ||
a.nodes[node] = struct{}{} | ||
} | ||
|
||
func (a *auditingTransaction) RemoveNode(node types.NodeName) { | ||
delete(a.nodes, node) | ||
} | ||
|
||
// Commit adds one final operation to the underlying consul transaction to | ||
// create an audit log record with the set of nodes that already have been | ||
// scheduled and nodes that will be scheduled as a part of this transaction by | ||
// the RC. Then it commits the transaction | ||
func (a *auditingTransaction) Commit(cancelFunc func(), txner transaction.Txner) error { | ||
details, err := audit.NewRCRetargetingEventDetails( | ||
a.podID, | ||
a.az, | ||
a.cn, | ||
a.Nodes(), | ||
) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
err = a.auditLogStore.Create( | ||
a.ctx, | ||
audit.RCRetargetingEvent, | ||
details, | ||
) | ||
if err != nil { | ||
return util.Errorf("could not add rc retargeting audit log to context: %s", err) | ||
} | ||
|
||
return transaction.Commit(a.ctx, cancelFunc, txner) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
package rc | ||
|
||
import ( | ||
"context" | ||
"encoding/json" | ||
"testing" | ||
|
||
"github.com/square/p2/pkg/audit" | ||
pc_fields "github.com/square/p2/pkg/pc/fields" | ||
rc_fields "github.com/square/p2/pkg/rc/fields" | ||
"github.com/square/p2/pkg/store/consul/auditlogstore" | ||
"github.com/square/p2/pkg/store/consul/consulutil" | ||
"github.com/square/p2/pkg/types" | ||
|
||
klabels "k8s.io/kubernetes/pkg/labels" | ||
) | ||
|
||
func TestAuditingTransaction(t *testing.T) { | ||
fixture := consulutil.NewFixture(t) | ||
defer fixture.Stop() | ||
|
||
auditLogStore := auditlogstore.NewConsulStore(fixture.Client.KV()) | ||
rc := &replicationController{ | ||
RC: rc_fields.RC{ | ||
Manifest: testManifest(), | ||
PodLabels: klabels.Set{ | ||
pc_fields.AvailabilityZoneLabel: "some_az", | ||
pc_fields.ClusterNameLabel: "some_cn", | ||
}, | ||
}, | ||
auditLogStore: auditLogStore, | ||
} | ||
|
||
ctx, cancel := rc.newAuditingTransaction(context.Background(), []types.NodeName{"node1", "node2"}) | ||
defer cancel() | ||
|
||
ctx.AddNode("node3") | ||
ctx.RemoveNode("node2") | ||
|
||
err := ctx.Commit(cancel, fixture.Client.KV()) | ||
if err != nil { | ||
t.Fatalf("could not commit audit log record: %s", err) | ||
} | ||
|
||
records, err := auditLogStore.List() | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
if len(records) != 1 { | ||
t.Fatalf("expected a single audit log record but there were %d", len(records)) | ||
} | ||
|
||
for _, record := range records { | ||
if record.EventType != audit.RCRetargetingEvent { | ||
t.Errorf("expected audit log type to be %q but was %q", audit.RCRetargetingEvent, record.EventType) | ||
} | ||
|
||
var details audit.RCRetargetingDetails | ||
err = json.Unmarshal([]byte(*record.EventDetails), &details) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
|
||
if details.PodID != testManifest().ID() { | ||
t.Errorf("expected audit log details to have pod ID %q but was %q", testManifest().ID(), details.PodID) | ||
} | ||
if details.AvailabilityZone != "some_az" { | ||
t.Errorf("expected audit log details to have availability zone %q but was %q", "some_az", details.AvailabilityZone) | ||
} | ||
if details.ClusterName != "some_cn" { | ||
t.Errorf("expected audit log details to have cluster name %q but was %q", "some_cn", details.ClusterName) | ||
} | ||
|
||
if len(details.Nodes) != 2 { | ||
t.Fatalf("expected 2 nodes but there were %d", len(details.Nodes)) | ||
} | ||
found1 := false | ||
found3 := false | ||
for _, node := range details.Nodes { | ||
switch node { | ||
case "node1": | ||
found1 = true | ||
case "node3": | ||
found3 = true | ||
default: | ||
t.Errorf("found node %s but that wasn't expected", node) | ||
} | ||
} | ||
|
||
if !found1 { | ||
t.Error("expected to find node1 in the list") | ||
} | ||
|
||
if !found3 { | ||
t.Error("expected to find node3 in the list") | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.