Skip to content

Commit 90d1f7c

Browse files
committed
mockgcp: support VertexAIDashboard
1 parent 64239bc commit 90d1f7c

File tree

10 files changed

+433
-56
lines changed

10 files changed

+433
-56
lines changed

config/tests/samples/create/harness.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -449,6 +449,8 @@ func MaybeSkip(t *testing.T, name string, resources []*unstructured.Unstructured
449449

450450
case schema.GroupKind{Group: "tags.cnrm.cloud.google.com", Kind: "TagsTagKey"}:
451451

452+
case schema.GroupKind{Group: "vertexai.cnrm.cloud.google.com", Kind: "VertexAITensorboard"}:
453+
452454
default:
453455
t.Skipf("gk %v not suppported by mock gcp; skipping", gvk.GroupKind())
454456
}

mockgcp/common/operations/http.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,15 +27,18 @@ import (
2727
"k8s.io/klog/v2"
2828
)
2929

30-
func (s *Operations) RegisterOperationsHandler(prefix string) func(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {
30+
func (s *Operations) RegisterOperationsPath(path string) func(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {
3131
return func(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {
3232
forwardResponseOptions := mux.GetForwardResponseOptions()
3333

34-
// GET /{prefix}/operations/{name}
35-
if err := mux.HandlePath("GET", "/"+prefix+"/operations/{name}", func(w http.ResponseWriter, r *http.Request, pathParams map[string]string) {
34+
if err := mux.HandlePath("GET", path, func(w http.ResponseWriter, r *http.Request, pathParams map[string]string) {
3635
ctx := r.Context()
3736
name := pathParams["name"]
37+
prefix := pathParams["prefix"]
3838
req := &longrunningpb.GetOperationRequest{Name: "operations/" + name}
39+
if prefix != "" {
40+
req.Name = prefix + "/operations/" + name
41+
}
3942
op, err := s.GetOperation(ctx, req)
4043
if err != nil {
4144
if status.Code(err) == codes.NotFound {

mockgcp/common/operations/operations.go

Lines changed: 66 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -62,14 +62,17 @@ func (s *Operations) NewLRO(ctx context.Context) (*pb.Operation, error) {
6262
return op, nil
6363
}
6464

65-
func (s *Operations) StartLRO(ctx context.Context, metadata proto.Message, callback func() (proto.Message, error)) (*pb.Operation, error) {
65+
func (s *Operations) StartLRO(ctx context.Context, prefix string, metadata proto.Message, callback func() (proto.Message, error)) (*pb.Operation, error) {
6666
now := time.Now()
6767
millis := now.UnixMilli()
6868
id := uuid.NewUUID()
6969

7070
op := &pb.Operation{}
7171

7272
op.Name = fmt.Sprintf("operations/operation-%d-%s", millis, id)
73+
if prefix != "" {
74+
op.Name = prefix + "/" + op.Name
75+
}
7376
op.Done = false
7477

7578
if metadata != nil {
@@ -95,26 +98,10 @@ func (s *Operations) StartLRO(ctx context.Context, metadata proto.Message, callb
9598
return
9699
}
97100

98-
finished.Done = true
99-
if err != nil {
100-
finished.Result = &pb.Operation_Error{
101-
Error: &rpcstatus.Status{
102-
Message: fmt.Sprintf("error processing operation: %v", err),
103-
},
104-
}
105-
} else {
106-
resultAny, err := anypb.New(result)
107-
if err != nil {
108-
klog.Warningf("error building anypb for result: %v", err)
109-
finished.Result = &pb.Operation_Response{}
110-
} else {
111-
rewriteTypes(resultAny)
112-
113-
finished.Result = &pb.Operation_Response{
114-
Response: resultAny,
115-
}
116-
}
101+
if err2 := markDone(finished, result, err); err2 != nil {
102+
klog.Warningf("error marking LRO as done: %v", err2)
117103
}
104+
118105
if err := s.storage.Update(ctx, fqn, finished); err != nil {
119106
klog.Warningf("error updating LRO: %v", err)
120107
return
@@ -124,6 +111,65 @@ func (s *Operations) StartLRO(ctx context.Context, metadata proto.Message, callb
124111
return op, nil
125112
}
126113

114+
func markDone(op *pb.Operation, result proto.Message, err error) error {
115+
op.Done = true
116+
if err != nil {
117+
op.Result = &pb.Operation_Error{
118+
Error: &rpcstatus.Status{
119+
Message: fmt.Sprintf("error processing operation: %v", err),
120+
},
121+
}
122+
} else {
123+
resultAny, err := anypb.New(result)
124+
if err != nil {
125+
klog.Warningf("error building anypb for result: %v", err)
126+
op.Result = &pb.Operation_Response{}
127+
} else {
128+
rewriteTypes(resultAny)
129+
130+
op.Result = &pb.Operation_Response{
131+
Response: resultAny,
132+
}
133+
}
134+
}
135+
return nil
136+
}
137+
138+
func (s *Operations) DoneLRO(ctx context.Context, prefix string, metadata proto.Message, result proto.Message) (*pb.Operation, error) {
139+
now := time.Now()
140+
millis := now.UnixMilli()
141+
id := uuid.NewUUID()
142+
143+
op := &pb.Operation{}
144+
145+
op.Name = fmt.Sprintf("operations/operation-%d-%s", millis, id)
146+
if prefix != "" {
147+
op.Name = prefix + "/" + op.Name
148+
}
149+
op.Done = false
150+
151+
if err := markDone(op, result, nil); err != nil {
152+
return nil, err
153+
}
154+
155+
if metadata != nil {
156+
metadataAny, err := anypb.New(metadata)
157+
if err != nil {
158+
return nil, fmt.Errorf("error building anypb for metadata: %w", err)
159+
}
160+
rewriteTypes(metadataAny)
161+
162+
op.Metadata = metadataAny
163+
}
164+
fqn := op.Name
165+
166+
if err := s.storage.Create(ctx, fqn, op); err != nil {
167+
return nil, status.Errorf(codes.Internal, "error creating LRO: %v", err)
168+
}
169+
170+
return op, nil
171+
}
172+
127173
func rewriteTypes(any *anypb.Any) {
128174
// Fix our mockgcp hack
129175
if strings.HasPrefix(any.TypeUrl, "type.googleapis.com/mockgcp.") {

mockgcp/common/projects/projects.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ func (n *ProjectName) String() string {
4646
return "projects/" + n.OriginalValue
4747
}
4848

49+
// ParseProjectName parses a string into a ProjectName.
50+
// The expected form is projects/<projectIDOrNumber>
4951
func ParseProjectName(name string) (*ProjectName, error) {
5052
tokens := strings.Split(name, "/")
5153
if len(tokens) == 2 && tokens[0] == "projects" {

mockgcp/mock_http_roundtrip.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import (
3232
"sigs.k8s.io/controller-runtime/pkg/client"
3333

3434
"github.com/GoogleCloudPlatform/k8s-config-connector/mockgcp/common"
35+
"github.com/GoogleCloudPlatform/k8s-config-connector/mockgcp/mockaiplatform"
3536
"github.com/GoogleCloudPlatform/k8s-config-connector/mockgcp/mockapikeys"
3637
"github.com/GoogleCloudPlatform/k8s-config-connector/mockgcp/mockbilling"
3738
"github.com/GoogleCloudPlatform/k8s-config-connector/mockgcp/mockcertificatemanager"
@@ -88,6 +89,7 @@ func NewMockRoundTripper(t *testing.T, k8sClient client.Client, storage storage.
8889
var services []MockService
8990

9091
services = append(services, resourcemanagerService)
92+
services = append(services, mockaiplatform.New(env, storage))
9193
services = append(services, mockapikeys.New(env, storage))
9294
services = append(services, mockbilling.New(env, storage))
9395
services = append(services, mockcertificatemanager.New(env, storage))

mockgcp/mockaiplatform/service.go

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
// Copyright 2024 Google LLC
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 mockaiplatform
16+
17+
import (
18+
"context"
19+
"net/http"
20+
21+
"github.com/GoogleCloudPlatform/k8s-config-connector/mockgcp/common"
22+
"github.com/GoogleCloudPlatform/k8s-config-connector/mockgcp/common/httpmux"
23+
"github.com/GoogleCloudPlatform/k8s-config-connector/mockgcp/common/operations"
24+
"github.com/GoogleCloudPlatform/k8s-config-connector/mockgcp/common/projects"
25+
"github.com/GoogleCloudPlatform/k8s-config-connector/mockgcp/pkg/storage"
26+
"google.golang.org/grpc"
27+
"sigs.k8s.io/controller-runtime/pkg/client"
28+
29+
pb "github.com/GoogleCloudPlatform/k8s-config-connector/mockgcp/generated/mockgcp/cloud/aiplatform/v1beta1"
30+
)
31+
32+
// MockService represents a mocked aiplatform service.
33+
type MockService struct {
34+
kube client.Client
35+
storage storage.Storage
36+
37+
projects projects.ProjectStore
38+
operations *operations.Operations
39+
}
40+
41+
// New creates a MockService.
42+
func New(env *common.MockEnvironment, storage storage.Storage) *MockService {
43+
s := &MockService{
44+
kube: env.GetKubeClient(),
45+
storage: storage,
46+
projects: env.GetProjects(),
47+
operations: operations.NewOperationsService(storage),
48+
}
49+
return s
50+
}
51+
52+
func (s *MockService) ExpectedHost() string {
53+
// TODO: Support more endpoints
54+
return "us-central1-aiplatform.googleapis.com"
55+
}
56+
57+
func (s *MockService) Register(grpcServer *grpc.Server) {
58+
pb.RegisterTensorboardServiceServer(grpcServer, &tensorboardService{MockService: s})
59+
}
60+
61+
func (s *MockService) NewHTTPMux(ctx context.Context, conn *grpc.ClientConn) (http.Handler, error) {
62+
mux, err := httpmux.NewServeMux(ctx, conn, pb.RegisterTensorboardServiceHandler,
63+
s.operations.RegisterOperationsPath("/v1beta1/{prefix=**}/operations/{name}"))
64+
if err != nil {
65+
return nil, err
66+
}
67+
68+
return mux, nil
69+
}

0 commit comments

Comments
 (0)