Skip to content

Commit

Permalink
Add action update
Browse files Browse the repository at this point in the history
Implement update action for rGraph Node.
For e2e test in cloud add support for update action
in helath check objects.
  • Loading branch information
kl52752 authored and bowei committed Jan 25, 2024
1 parent b74c568 commit 8e74dd9
Show file tree
Hide file tree
Showing 2 changed files with 293 additions and 6 deletions.
98 changes: 92 additions & 6 deletions pkg/cloud/rgraph/rnode/action_update.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,34 +18,120 @@ package rnode

import (
"context"
"fmt"
"time"

"github.com/GoogleCloudPlatform/k8s-cloud-provider/pkg/cloud"
"github.com/GoogleCloudPlatform/k8s-cloud-provider/pkg/cloud/api"
"github.com/GoogleCloudPlatform/k8s-cloud-provider/pkg/cloud/meta"
"github.com/GoogleCloudPlatform/k8s-cloud-provider/pkg/cloud/rgraph/exec"
)

// TODO
func UpdateActions[GA any, Alpha any, Beta any](
ops GenericOps[GA, Alpha, Beta],
got, want Node,
resource api.Resource[GA, Alpha, Beta],
) ([]exec.Action, error) {
preEvents, err := updatePreconditions(got, want)
if err != nil {
return nil, err
}
postEvents := postUpdateActionEvents(got, want)
return []exec.Action{
newGenericUpdateAction(preEvents, ops, want.ID(), resource, postEvents),
}, nil
}

func newGenericUpdateAction[GA any, Alpha any, Beta any](
want exec.EventList,
ops GenericOps[GA, Alpha, Beta],
id *cloud.ResourceID,
resource api.Resource[GA, Alpha, Beta],
postEvents exec.EventList,
) *genericUpdateAction[GA, Alpha, Beta] {
return &genericUpdateAction[GA, Alpha, Beta]{
ActionBase: exec.ActionBase{Want: want},
ops: ops,
id: id,
resource: resource,
postEvents: postEvents,
}
}

type genericUpdateAction[GA any, Alpha any, Beta any] struct {
exec.ActionBase
ops GenericOps[GA, Alpha, Beta]
id *cloud.ResourceID
resource api.Resource[GA, Alpha, Beta]
postEvents exec.EventList

start, end time.Time
}

func (a *genericUpdateAction[GA, Alpha, Beta]) Run(
ctx context.Context,
c cloud.Cloud,
) (exec.EventList, error) {
return nil, nil
a.start = time.Now()
err := a.ops.UpdateFuncs(c).Do(ctx, "", a.id, a.resource)
a.end = time.Now()

// Emit DropReference events for removed references.
return a.postEvents, err
}

func (a *genericUpdateAction[GA, Alpha, Beta]) DryRun() exec.EventList {
return nil
// Emit DropReference events for removed references.
return a.postEvents
}

func (a *genericUpdateAction[GA, Alpha, Beta]) String() string {
return "GenericUpdateAction TODO"
return fmt.Sprintf("GenericUpdateAction(%v)", a.id)
}

func (a *genericUpdateAction[GA, Alpha, Beta]) Metadata() *exec.ActionMetadata {
return &exec.ActionMetadata{
Name: fmt.Sprintf("GenericUpdateAction(%s)", a.id),
Type: exec.ActionTypeUpdate,
Summary: fmt.Sprintf("Update %s", a.id),
}
}

func updatePreconditions(got, want Node) exec.EventList {
func updatePreconditions(got, want Node) (exec.EventList, error) {
// Update can only occur if the resource Exists TODO: is there a case where
// the ambient signal for existance from Update op collides with a
// reference to it?
return nil // TODO: finish me
if got.State() != NodeExists || want.State() != NodeExists {
return nil, fmt.Errorf("node for update does not exist")
}

outRefs := want.OutRefs()
var events exec.EventList
// Condition: references must exist before update.
for _, ref := range outRefs {
events = append(events, exec.NewExistsEvent(ref.To))
}
return events, nil
}

func postUpdateActionEvents(got, want Node) exec.EventList {
wantOutRefs := want.OutRefs()
gotOutRefs := got.OutRefs()

wantRefs := make(map[meta.Key]struct{})
for _, r := range wantOutRefs {
var empty struct{}
wantRefs[*r.To.Key] = empty
}

// Drop reference for resources that does not exists in want Node.
var events exec.EventList
for _, wantRef := range gotOutRefs {
_, ok := wantRefs[*wantRef.To.Key]
if !ok {
events = append(events, exec.NewDropRefEvent(wantRef.From, wantRef.To))
}
}

return events
}
201 changes: 201 additions & 0 deletions pkg/cloud/rgraph/rnode/action_update_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
/*
Copyright 2023 Google LLC
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
https://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 rnode

import (
"testing"

"github.com/GoogleCloudPlatform/k8s-cloud-provider/pkg/cloud"
"github.com/GoogleCloudPlatform/k8s-cloud-provider/pkg/cloud/meta"
"github.com/GoogleCloudPlatform/k8s-cloud-provider/pkg/cloud/rgraph/exec"
)

const project = "proj-id"

func globalID(name string) *cloud.ResourceID {
key := meta.GlobalKey(name)
return &cloud.ResourceID{
Resource: "node",
APIGroup: "",
ProjectID: project,
Key: key,
}
}

func createFakeNode(toRefs []string) Node {
fn := fakeNode{}
fn.ownership = OwnershipManaged
fn.state = NodeExists
fn.id = globalID("fn")
for _, to := range toRefs {
outRef := ResourceRef{
From: fn.id,
To: globalID(to),
}
fn.outRefs = append(fn.outRefs, outRef)
}
return &fn
}

func createNotExistingFakeNode() Node {
fn := fakeNode{}
fn.ownership = OwnershipManaged
fn.state = NodeDoesNotExist
fn.id = globalID("fn")
return &fn
}

func dropRefEventList(toRefs []string) exec.EventList {
var events exec.EventList
from := globalID("fn")
for _, to := range toRefs {
events = append(events, exec.NewDropRefEvent(from, globalID(to)))
}
return events
}

func newExistsEventList(toRefs []string) exec.EventList {
var events exec.EventList
for _, to := range toRefs {
events = append(events, exec.NewExistsEvent(globalID(to)))
}
return events
}

func TestPostUpdateActions(t *testing.T) {
for _, tc := range []struct {
desc string
oldNode Node
newNode Node
wantEvents exec.EventList
wantErr bool
}{
{
desc: "node's without outefs",
oldNode: createFakeNode(nil),
newNode: createFakeNode(nil),
},
{
desc: "node's with added outefs",
oldNode: createFakeNode(nil),
newNode: createFakeNode([]string{"a", "b"}),
wantEvents: newExistsEventList([]string{"a", "b"}),
},
{
desc: "node's with deleted outefs",
oldNode: createFakeNode([]string{"a", "b"}),
newNode: createFakeNode(nil),
},
{
desc: "node's with replaced outef",
oldNode: createFakeNode([]string{"a", "b"}),
newNode: createFakeNode([]string{"a", "c"}),
wantEvents: newExistsEventList([]string{"a", "c"}),
},
{
desc: "nodes don't exist",
oldNode: createNotExistingFakeNode(),
newNode: createNotExistingFakeNode(),
wantErr: true,
},
{
desc: "new node doesn't exist",
oldNode: createFakeNode([]string{"a", "b"}),
newNode: createNotExistingFakeNode(),
wantErr: true,
},
} {
t.Run(tc.desc, func(t *testing.T) {

gotEvents, err := updatePreconditions(tc.oldNode, tc.newNode)
gotErr := err != nil
if gotErr != tc.wantErr {
t.Errorf("updatePreconditions(_, _) = %v, want %v", gotErr, tc.wantErr)
}
if tc.wantErr {
return
}
if len(gotEvents) != len(tc.wantEvents) {
t.Fatalf("event's length mismatch, got: %d, want: %d", len(gotEvents), len(tc.wantEvents))
}
for i, gotEvent := range gotEvents {
wantEvent := tc.wantEvents[i]
if !gotEvent.Equal(wantEvent) {
t.Errorf("%v != %v", gotEvent.String(), wantEvent.String())
}
}
})
}
}

func TestUpdatePreconditions(t *testing.T) {
for _, tc := range []struct {
desc string
oldNode Node
newNode Node
wantEvents exec.EventList
}{
{
desc: "node's without outefs",
oldNode: createFakeNode(nil),
newNode: createFakeNode(nil),
},
{
desc: "node's with added outefs",
oldNode: createFakeNode(nil),
newNode: createFakeNode([]string{"a", "b"}),
},
{
desc: "node's with deleted outefs",
oldNode: createFakeNode([]string{"a", "b"}),
newNode: createFakeNode(nil),
wantEvents: dropRefEventList([]string{"a", "b"}),
},
{
desc: "node's with deleted first outefs",
oldNode: createFakeNode([]string{"a", "b"}),
newNode: createFakeNode([]string{"b"}),
wantEvents: dropRefEventList([]string{"a"}),
},
{
desc: "node's with deleted outefs, random order",
oldNode: createFakeNode([]string{"a", "b", "c"}),
newNode: createFakeNode([]string{"b", "a"}),
wantEvents: dropRefEventList([]string{"c"}),
},
{
desc: "node's with replaced outefs",
oldNode: createFakeNode([]string{"a", "b", "c"}),
newNode: createFakeNode([]string{"a", "b", "e"}),
wantEvents: dropRefEventList([]string{"c"}),
},
} {
t.Run(tc.desc, func(t *testing.T) {

gotEvents := postUpdateActionEvents(tc.oldNode, tc.newNode)
if len(gotEvents) != len(tc.wantEvents) {
t.Fatalf("postUpdateActionEvents(got, want) = %d, want %d", len(gotEvents), len(tc.wantEvents))
}
for i, gotEvent := range gotEvents {
wantEvent := tc.wantEvents[i]
if !gotEvent.Equal(wantEvent) {
t.Errorf("%v != %v", gotEvent.String(), wantEvent.String())
}
}
})
}
}

0 comments on commit 8e74dd9

Please sign in to comment.