forked from GoogleCloudPlatform/k8s-config-connector
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
direct controller for ComputeForwardingRule
- Loading branch information
Showing
6 changed files
with
638 additions
and
1 deletion.
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
Binary file not shown.
302 changes: 302 additions & 0 deletions
302
pkg/controller/direct/compute/forwardingrule_controller.go
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,302 @@ | ||
// Copyright 2024 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 | ||
// | ||
// 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 compute | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
refs "github.com/GoogleCloudPlatform/k8s-config-connector/apis/refs/v1beta1" | ||
"reflect" | ||
|
||
gcp "cloud.google.com/go/compute/apiv1" | ||
computepb "cloud.google.com/go/compute/apiv1/computepb" | ||
krm "github.com/GoogleCloudPlatform/k8s-config-connector/apis/compute/v1beta1" | ||
"github.com/GoogleCloudPlatform/k8s-config-connector/pkg/config" | ||
"github.com/GoogleCloudPlatform/k8s-config-connector/pkg/controller/direct" | ||
"github.com/GoogleCloudPlatform/k8s-config-connector/pkg/controller/direct/directbase" | ||
"github.com/GoogleCloudPlatform/k8s-config-connector/pkg/controller/direct/registry" | ||
"google.golang.org/api/option" | ||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" | ||
"k8s.io/apimachinery/pkg/runtime" | ||
"k8s.io/klog/v2" | ||
"sigs.k8s.io/controller-runtime/pkg/client" | ||
) | ||
|
||
func init() { | ||
registry.RegisterModel(krm.GroupVersionKind, NewForwardingRuleModel) | ||
} | ||
|
||
func NewForwardingRuleModel(ctx context.Context, config *config.ControllerConfig) (directbase.Model, error) { | ||
return &forwardingRuleModel{config: config}, nil | ||
} | ||
|
||
type forwardingRuleModel struct { | ||
config *config.ControllerConfig | ||
} | ||
|
||
// model implements the Model interface. | ||
var _ directbase.Model = &forwardingRuleModel{} | ||
|
||
type forwardingRuleAdapter struct { | ||
resourceID string | ||
projectID string | ||
Location string | ||
gcpClient *gcp.ForwardingRulesClient | ||
desired *krm.ComputeForwardingRule | ||
actual *computepb.ForwardingRule | ||
} | ||
|
||
var _ directbase.Adapter = &forwardingRuleAdapter{} | ||
|
||
func (m *forwardingRuleModel) forwardingRulesClient(ctx context.Context) (*gcp.ForwardingRulesClient, error) { | ||
var opts []option.ClientOption | ||
if m.config.UserAgent != "" { | ||
opts = append(opts, option.WithUserAgent(m.config.UserAgent)) | ||
} | ||
if m.config.HTTPClient != nil { | ||
opts = append(opts, option.WithHTTPClient(m.config.HTTPClient)) | ||
} | ||
if m.config.UserProjectOverride && m.config.BillingProject != "" { | ||
opts = append(opts, option.WithQuotaProject(m.config.BillingProject)) | ||
} | ||
|
||
gcpClient, err := gcp.NewForwardingRulesRESTClient(ctx, opts...) | ||
if err != nil { | ||
return nil, fmt.Errorf("building ComputeForwardingRule client: %w", err) | ||
} | ||
return gcpClient, err | ||
} | ||
|
||
func (m *forwardingRuleModel) AdapterForObject(ctx context.Context, reader client.Reader, u *unstructured.Unstructured) (directbase.Adapter, error) { | ||
obj := &krm.ComputeForwardingRule{} | ||
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(u.Object, &obj); err != nil { | ||
return nil, fmt.Errorf("error converting to %T: %w", obj, err) | ||
} | ||
|
||
// Get ResourceID | ||
resourceID, err := refs.GetResourceID(u) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
// Get projectID | ||
projectID, err := refs.ResolveProjectID(ctx, reader, u) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
// Get network | ||
if obj.Spec.NetworkRef != nil { | ||
networkRef, err := refs.ResolveComputeNetwork(ctx, reader, obj, obj.Spec.NetworkRef) | ||
if err != nil { | ||
return nil, err | ||
|
||
} | ||
obj.Spec.NetworkRef.External = networkRef.String() | ||
} | ||
|
||
// Get target targetHTTPProxy | ||
if obj.Spec.Target.TargetHTTPProxyRef != nil { | ||
targetHTTPProxyRef, err := refs.ResolveTargetHTTPProxy(ctx, reader, obj, obj.Spec.Target.TargetHTTPProxyRef) | ||
if err != nil { | ||
return nil, err | ||
|
||
} | ||
obj.Spec.Target.TargetHTTPProxyRef.External = targetHTTPProxyRef.String() | ||
} | ||
|
||
// Get location | ||
location := obj.Spec.Location | ||
|
||
// Get compute GCP client | ||
gcpClient, err := m.forwardingRulesClient(ctx) | ||
if err != nil { | ||
return nil, err | ||
} | ||
return &forwardingRuleAdapter{ | ||
resourceID: resourceID, | ||
projectID: projectID, | ||
Location: location, | ||
gcpClient: gcpClient, | ||
desired: obj, | ||
}, nil | ||
} | ||
|
||
func (m *forwardingRuleModel) AdapterForURL(ctx context.Context, url string) (directbase.Adapter, error) { | ||
// TODO: Support URLs | ||
return nil, nil | ||
} | ||
|
||
func (a *forwardingRuleAdapter) Find(ctx context.Context) (bool, error) { | ||
log := klog.FromContext(ctx).WithName(ctrlName) | ||
log.V(2).Info("getting ComputeForwardingRule", "name", a.fullyQualifiedName()) | ||
|
||
if a.resourceID == "" { | ||
return false, nil | ||
} | ||
|
||
req := &computepb.GetForwardingRuleRequest{ | ||
ForwardingRule: a.fullyQualifiedName(), | ||
Project: a.projectID, | ||
} | ||
forwardingRule, err := a.gcpClient.Get(ctx, req) | ||
if err != nil { | ||
if direct.IsNotFound(err) { | ||
return false, nil | ||
} | ||
return false, fmt.Errorf("getting ComputeForwardingRule %q failed: %w", a.fullyQualifiedName(), err) | ||
} | ||
|
||
a.actual = forwardingRule | ||
return true, nil | ||
} | ||
|
||
func (a *forwardingRuleAdapter) Create(ctx context.Context, u *unstructured.Unstructured) error { | ||
log := klog.FromContext(ctx).WithName(ctrlName) | ||
log.V(2).Info("creating ComputeForwardingRule", "name", a.fullyQualifiedName()) | ||
mapCtx := &direct.MapContext{} | ||
|
||
projectID := a.projectID | ||
if projectID == "" { | ||
return fmt.Errorf("project is empty") | ||
} | ||
if a.resourceID == "" { | ||
return fmt.Errorf("resourceID is empty") | ||
} | ||
|
||
desired := a.desired.DeepCopy() | ||
|
||
forwardingRule := ComputeForwardingRuleSpec_ToProto(mapCtx, &desired.Spec) | ||
if mapCtx.Err() != nil { | ||
return mapCtx.Err() | ||
} | ||
|
||
req := &computepb.InsertForwardingRuleRequest{ | ||
ForwardingRuleResource: forwardingRule, | ||
Region: a.Location, | ||
Project: a.projectID, | ||
} | ||
op, err := a.gcpClient.Insert(ctx, req) | ||
if err != nil { | ||
return fmt.Errorf("creating ComputeForwardingRule %s failed: %w", a.fullyQualifiedName(), err) | ||
} | ||
log.V(2).Info("successfully created ComputeForwardingRule", "name", a.fullyQualifiedName()) | ||
|
||
status := &krm.ComputeForwardingRuleStatus{} | ||
// TODO(yuhou): Do we need other status beside conditions and observedGeneration? | ||
status.CreationTimestamp = op.Proto().CreationTimestamp | ||
status.SelfLink = direct.LazyPtr(a.fullyQualifiedName()) | ||
return setStatus(u, status) | ||
} | ||
|
||
func (a *forwardingRuleAdapter) Update(ctx context.Context, u *unstructured.Unstructured) error { | ||
log := klog.FromContext(ctx).WithName(ctrlName) | ||
log.V(2).Info("updating ComputeForwardingRule", "name", a.fullyQualifiedName()) | ||
mapCtx := &direct.MapContext{} | ||
|
||
desired := a.desired.DeepCopy() | ||
forwardingRule := ComputeForwardingRuleSpec_ToProto(mapCtx, &desired.Spec) | ||
if mapCtx.Err() != nil { | ||
return mapCtx.Err() | ||
} | ||
|
||
status := &krm.ComputeForwardingRuleStatus{} | ||
// Patch only support update on networkTier field, which KCC does not support yet. | ||
// Use setTarget and setLabels to update target and labels field. | ||
if !reflect.DeepEqual(forwardingRule.Target, a.actual.Target) { | ||
setTargetReq := &computepb.SetTargetForwardingRuleRequest{ | ||
ForwardingRule: a.resourceID, | ||
TargetReferenceResource: &computepb.TargetReference{Target: forwardingRule.Target}, | ||
Project: a.projectID, | ||
Region: a.Location, | ||
} | ||
op, err := a.gcpClient.SetTarget(ctx, setTargetReq) | ||
if err != nil { | ||
return fmt.Errorf("updating ComputeForwardingRule target %s failed: %w", a.fullyQualifiedName(), err) | ||
} | ||
log.V(2).Info("successfully updated ComputeForwardingRule target", "name", a.fullyQualifiedName()) | ||
|
||
// TODO(yuhou): Do we need other status beside conditions and observedGeneration? | ||
status.CreationTimestamp = op.Proto().CreationTimestamp | ||
status.SelfLink = direct.LazyPtr(a.fullyQualifiedName()) | ||
} | ||
|
||
if !reflect.DeepEqual(forwardingRule.Labels, a.actual.Labels) { | ||
setLabelsReq := &computepb.SetLabelsForwardingRuleRequest{ | ||
Resource: a.resourceID, | ||
RegionSetLabelsRequestResource: &computepb.RegionSetLabelsRequest{Labels: forwardingRule.Labels}, | ||
Project: a.projectID, | ||
Region: a.Location, | ||
} | ||
op, err := a.gcpClient.SetLabels(ctx, setLabelsReq) | ||
if err != nil { | ||
return fmt.Errorf("updating ComputeForwardingRule labels %s failed: %w", a.fullyQualifiedName(), err) | ||
} | ||
log.V(2).Info("successfully updated ComputeForwardingRule labels", "name", a.fullyQualifiedName()) | ||
|
||
// TODO(yuhou): Do we need other status beside conditions and observedGeneration? | ||
status.CreationTimestamp = op.Proto().CreationTimestamp | ||
status.SelfLink = direct.LazyPtr(a.fullyQualifiedName()) | ||
} | ||
return setStatus(u, status) | ||
} | ||
|
||
func (a *forwardingRuleAdapter) Export(ctx context.Context) (*unstructured.Unstructured, error) { | ||
// TODO(kcc) | ||
return nil, nil | ||
} | ||
|
||
// Delete implements the Adapter interface. | ||
func (a *forwardingRuleAdapter) Delete(ctx context.Context) (bool, error) { | ||
log := klog.FromContext(ctx).WithName(ctrlName) | ||
log.V(2).Info("deleting ComputeForwardingRule", "name", a.fullyQualifiedName()) | ||
|
||
exist, err := a.Find(ctx) | ||
if err != nil { | ||
return false, err | ||
} | ||
if !exist { | ||
// return (false, nil) if the object was not found but should be presumed deleted. | ||
return false, nil | ||
} | ||
|
||
if a.resourceID == "" { | ||
return false, nil | ||
} | ||
req := &computepb.DeleteForwardingRuleRequest{ | ||
ForwardingRule: a.fullyQualifiedName(), | ||
Region: a.Location, | ||
Project: a.projectID, | ||
} | ||
op, err := a.gcpClient.Delete(ctx, req) | ||
if err != nil { | ||
return false, fmt.Errorf("deleting ComputeForwardingRule %s failed: %w", a.fullyQualifiedName(), err) | ||
} | ||
err = op.Wait(ctx) | ||
if err != nil { | ||
return false, fmt.Errorf("waiting delete ComputeForwardingRule %s failed: %w", a.fullyQualifiedName(), err) | ||
} | ||
log.V(2).Info("successfully deleted ComputeForwardingRule", "name", a.fullyQualifiedName()) | ||
return true, nil | ||
} | ||
|
||
func (a *forwardingRuleAdapter) fullyQualifiedName() string { | ||
return fmt.Sprintf("projects/%s/location/%s/forwardingRules/%s", a.projectID, a.Location, a.resourceID) | ||
} | ||
|
||
func (a *forwardingRuleAdapter) getParent() string { | ||
// TODO(user): Write the GCP URI parent for your resource | ||
return fmt.Sprintf("projects/%s", a.projectID) | ||
} |
Oops, something went wrong.