Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
66 commits
Select commit Hold shift + click to select a range
5b7b610
Adding approved intents types
evyatarmeged Jan 26, 2025
c42bca3
Adding approved client intents crd & changes to existing client intents
evyatarmeged Jan 26, 2025
110130c
Adding approval flow for OSS (auto approve) and a reconciler for appr…
evyatarmeged Jan 26, 2025
5b8e437
tweaks to main with revamped intents reconciler
evyatarmeged Jan 26, 2025
52e917c
Merge branch 'main' of https://github.com/otterize/intents-operator i…
evyatarmeged Jan 26, 2025
f59a2e2
Adding cloud approval flow and some genqlient tweaks
evyatarmeged Jan 27, 2025
95cd18c
Changing everything to approved client intents model
evyatarmeged Jan 27, 2025
c2a1ba6
Adding periodic approval flow
evyatarmeged Jan 27, 2025
d8593fd
Adding polling for approval updates
evyatarmeged Jan 28, 2025
9def051
demo commit
evyatarmeged Jan 28, 2025
02855f6
latest fixes
evyatarmeged Jan 29, 2025
269cd19
fixes
evyatarmeged Jan 29, 2025
e226fcb
function updates
evyatarmeged Jan 29, 2025
ee676e9
Making resource UID the hash for intents approval reporting
evyatarmeged Jan 30, 2025
089beb1
Copying old status fields from client intents to approved client intents
evyatarmeged Jan 30, 2025
9be3ee7
Added old status update to approved client intents
evyatarmeged Feb 1, 2025
2ce015c
Refactor client intents handling to use approved intents and update G…
sapirwo Feb 11, 2025
607771d
Move to v2beta1, write webhooks
omris94 Feb 16, 2025
89267a0
Merge remote-tracking branch 'origin/main' into evya/approved_intents…
omris94 Feb 16, 2025
2f79886
fixup
omris94 Feb 17, 2025
5854619
Align with the Cloud API
omris94 Feb 19, 2025
5adf83e
Make tests work
omris94 Feb 19, 2025
edd567b
uncomment
omris94 Feb 19, 2025
6b1fa5b
Fix CRDs
omris94 Feb 20, 2025
f40a859
Add delete and update logic
omris94 Feb 24, 2025
8772e86
Merge remote-tracking branch 'origin/main' into evya/approved_intents…
omris94 Feb 24, 2025
77d6a51
Do it idempotent
omris94 Feb 24, 2025
be9e44e
Fix updates
omris94 Feb 25, 2025
f16a610
Add tests improve upToDate logic
omris94 Feb 25, 2025
66e4adc
Remove deprecation
omris94 Feb 26, 2025
26e0d5c
Merge remote-tracking branch 'origin/main' into evya/approved_intents…
omris94 Feb 26, 2025
04d771a
bugfix isShared
omris94 Feb 26, 2025
3831311
helm
omris94 Feb 26, 2025
e2d0592
update makefile
omris94 Feb 26, 2025
75eac3d
order
omris94 Feb 26, 2025
937bc85
reorder
omris94 Feb 26, 2025
aa0a712
remove use of upToDate; add events
omris94 Feb 26, 2025
d2e2689
Mirror events from approvedClientIntents to ClientIntents
omris94 Mar 10, 2025
98ace92
Add events for status review changes
omris94 Mar 11, 2025
f9a94d6
Add upToDateReconcilers who watches ApprovedClientIntents aligns thei…
omris94 Mar 11, 2025
fe0fe76
Approved client intents migration - The webhook will create approvedC…
omris94 Mar 13, 2025
8ac6005
Use finalizer with clientIntents -
omris94 Mar 13, 2025
15e40eb
Add config for cloud approval
omris94 Mar 13, 2025
4d8c424
Use the same name for intents and approved intents
omris94 Mar 13, 2025
da7a996
Align with cloud API
omris94 Mar 16, 2025
6d8ab8e
Merge remote-tracking branch 'origin/main' into evya/approved_intents…
omris94 Mar 17, 2025
f94286c
fixup makefile
omris94 Mar 17, 2025
b2347fc
generate
omris94 Mar 17, 2025
74a7dbd
fixup
omris94 Mar 17, 2025
599debe
fixup
omris94 Mar 17, 2025
ffb98a6
lint fix
omris94 Mar 17, 2025
0f7ad84
Update helm
omris94 Mar 18, 2025
d571172
Merge remote-tracking branch 'origin/main' into evya/approved_intents…
omris94 Mar 18, 2025
07f990c
Add resolved IPs to the migration
omris94 Mar 18, 2025
d7a6a32
fix events strings
omris94 Mar 20, 2025
21fcd63
remove not in use
omris94 Mar 20, 2025
6c30be8
Merge remote-tracking branch 'origin/main' into evya/approved_intents…
omris94 Jun 8, 2025
8d052e4
Update src/operator/controllers/approved_intents_controller.go
omris94 Jun 8, 2025
020624b
Update src/operator/controllers/approved_intents_controller.go
omris94 Jun 8, 2025
b4bc4a0
Update src/operator/controllers/approved_intents_controller.go
omris94 Jun 8, 2025
c5fd900
Update src/operator/controllers/approved_intents_controller.go
omris94 Jun 8, 2025
1a18276
fixup
omris94 Jun 8, 2025
10b6a51
fixup
omris94 Jun 8, 2025
47555e7
fixup
omris94 Jun 8, 2025
fc101fb
fixup
omris94 Jun 9, 2025
4c0beca
Fix tests
omris94 Jun 11, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@
generated.go linguist-generated=true
go.mod linguist-generated=true
go.sum linguist-generated=true
schema.graphql linguist-generated=true
schema.graphql linguist-generated=true
*.patched linguist-generated=true
5 changes: 5 additions & 0 deletions src/operator/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ IMG ?= $(IMAGE_TAG_BASE):operator-$(VERSION)

LOCAL_IMAGE_TAG ?= 0.0.0
OPERATOR_DEBUG_IMAGE ?= otterize/intents-operator:$(LOCAL_IMAGE_TAG)
OPERATOR_WEBHOOK_DEBUG_IMAGE ?= otterize/intents-operator-webhook-server:$(LOCAL_IMAGE_TAG)

# ENVTEST_K8S_VERSION refers to the version of kubebuilder assets to be downloaded by envtest binary.
ENVTEST_K8S_VERSION = 1.24.1
Expand Down Expand Up @@ -99,6 +100,7 @@ docker-build: test ## Build docker image with the manager.
.PHONY: docker-build-local
docker-build-local: test ## Build docker image with the manager.
docker build --build-arg="VERSION=$(LOCAL_IMAGE_TAG)" -t ${OPERATOR_DEBUG_IMAGE} -f ../intents-operator.Dockerfile ../
docker build --build-arg="VERSION=$(LOCAL_IMAGE_TAG)" -t ${OPERATOR_WEBHOOK_DEBUG_IMAGE} -f ../intents-operator-webhook-server.Dockerfile ../


.PHONY: docker-push
Expand All @@ -109,6 +111,8 @@ docker-push: ## Push docker image with the manager.
minikube-push: ## For MacOS users: Push locally built docker images directly into minikube VM since it doesn't access local docker registry directly.
minikube ssh "docker rmi -f $(OPERATOR_DEBUG_IMAGE)"
minikube image load ${OPERATOR_DEBUG_IMAGE}
minikube ssh "docker rmi -f $(OPERATOR_WEBHOOK_DEBUG_IMAGE)"
minikube image load ${OPERATOR_WEBHOOK_DEBUG_IMAGE}


##@ Deployment
Expand Down Expand Up @@ -138,6 +142,7 @@ deploy: manifests copy-manifests-to-helm helm-dependency
deploy-local: manifests copy-manifests-to-helm helm-dependency ## Deploy images built locally into the cluster.
helm upgrade --install -n otterize-system --create-namespace otterize $(OTTERIZE_HELM_CHART_DIR) \
--set intentsOperator.operator.tag=$(LOCAL_IMAGE_TAG) \
--set intentsOperator.webhookServer.tag=$(LOCAL_IMAGE_TAG) \
--set intentsOperator.operator.pullPolicy=Never \
--set global.telemetry.enabled=false

Expand Down
235 changes: 235 additions & 0 deletions src/operator/api/v2alpha1/approved_clientintents_types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,235 @@
package v2alpha1

import (
"context"
"encoding/json"
"fmt"
"github.com/otterize/intents-operator/src/shared/errors"
"github.com/otterize/intents-operator/src/shared/otterizecloud/graphqlclient"
"github.com/otterize/intents-operator/src/shared/serviceidresolver/serviceidentity"
"github.com/samber/lo"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/sets"
"sigs.k8s.io/controller-runtime/pkg/client"
"strconv"
)

func init() {
SchemeBuilder.Register(&ApprovedClientIntents{}, &ApprovedClientIntentsList{})
}

//+kubebuilder:object:root=true
//+kubebuilder:subresource:status

type ApprovedClientIntents struct {
metav1.TypeMeta `json:",inline" yaml:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty" yaml:"metadata,omitempty"`

Spec *IntentsSpec `json:"spec,omitempty" yaml:"spec,omitempty"`
Status ApprovedClientIntentsStatus `json:"status,omitempty" yaml:"status,omitempty"`
}

// ApprovedClientIntentsList contains a list of ApprovedClientIntents
type ApprovedClientIntentsList struct {
metav1.TypeMeta `json:",inline" yaml:",inline"`
metav1.ListMeta `json:"metadata,omitempty" yaml:"metadata,omitempty"`
Items []ApprovedClientIntents `json:"items" yaml:"items"`
}

func init() {
SchemeBuilder.Register(&ApprovedClientIntents{}, &ApprovedClientIntentsList{})
}

func (in *ApprovedClientIntentsList) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}

type ApprovedClientIntentsStatus struct {
// upToDate field reflects whether the client intents have successfully been applied
// to the cluster to the state specified
// +optional
UpToDate bool `json:"upToDate"`
// The last generation of the intents that was successfully reconciled.
// +optional
ObservedGeneration int64 `json:"observedGeneration"`
// ResolvedIPs stores resolved IPs for a domain name - the network mapper populates it when DNS internetTarget is used
// +optional
ResolvedIPs []ResolvedIPs `json:"resolvedIPs,omitempty" yaml:"resolvedIPs,omitempty"`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't forget to open a PR to update the mapper with this change + release them together

}

func (in *ApprovedClientIntents) GetWorkloadName() string {
return in.Spec.Workload.Name
}

func (in *ApprovedClientIntents) GetTargetList() []Target {
return in.Spec.Targets
}

func (in *ApprovedClientIntents) GetFilteredTargetList(intentTypes ...IntentType) []Target {
return lo.Filter(in.GetTargetList(), func(item Target, index int) bool {
for _, intentType := range intentTypes {
if intentType == IntentTypeHTTP {
if item.Kubernetes != nil && len(item.Kubernetes.HTTP) > 0 {
return true
}
if item.Service != nil && len(item.Service.HTTP) > 0 {
return true
}
}
if intentType == IntentTypeKafka && item.Kafka != nil {
return true
}
if intentType == IntentTypeDatabase && item.SQL != nil {
return true
}
if intentType == IntentTypeAWS && item.AWS != nil {
return true
}
if intentType == IntentTypeGCP && item.GCP != nil {
return true
}
if intentType == IntentTypeAzure && item.Azure != nil {
return true
}
if intentType == IntentTypeInternet && item.Internet != nil {
return true
}
}
return false
})
}

func (in *ApprovedClientIntents) GetClientKind() string {
if in.Spec.Workload.Kind == "" {
return serviceidentity.KindOtterizeLegacy
}
return in.Spec.Workload.Kind
}

func (in *ApprovedClientIntents) GetIntentsLabelMapping(requestNamespace string) map[string]string {
otterizeAccessLabels := make(map[string]string)

for _, intent := range in.GetTargetList() {
if intent.IsTargetOutOfCluster() {
continue
}
targetServiceIdentity := intent.ToServiceIdentity(requestNamespace)
labelKey := fmt.Sprintf(OtterizeAccessLabelKey, targetServiceIdentity.GetFormattedOtterizeIdentityWithKind())
if intent.IsTargetServerKubernetesService() {
labelKey = fmt.Sprintf(OtterizeSvcAccessLabelKey, targetServiceIdentity.GetFormattedOtterizeIdentityWithKind())
}
otterizeAccessLabels[labelKey] = "true"
}

return otterizeAccessLabels
}

func (in *ApprovedClientIntents) GetServersWithoutSidecar() (sets.Set[string], error) {
if in.Annotations == nil {
return sets.New[string](), nil
}

servers, ok := in.Annotations[OtterizeServersWithoutSidecarAnnotation]
if !ok {
return sets.New[string](), nil
}

serversList := make([]string, 0)
err := json.Unmarshal([]byte(servers), &serversList)
if err != nil {
return nil, errors.Wrap(err)
}

return sets.New[string](serversList...), nil
}

func (in *ApprovedClientIntents) GetDatabaseIntents() []Target {
return in.GetFilteredTargetList(IntentTypeDatabase)
}

func (in *ApprovedClientIntents) FromClientIntents(intents ClientIntents) {
in.Name = intents.Name
in.Namespace = intents.GetNamespace()
in.Spec = intents.Spec
}

func (in *ApprovedClientIntentsList) FormatAsOtterizeIntents(ctx context.Context, k8sClient client.Client) ([]*graphqlclient.IntentInput, error) {
otterizeIntents := make([]*graphqlclient.IntentInput, 0)
for _, clientIntents := range in.Items {
for _, intent := range clientIntents.GetTargetList() {
clientServiceIdentity := clientIntents.ToServiceIdentity()
input, err := intent.ConvertToCloudFormat(ctx, k8sClient, clientServiceIdentity)
if err != nil {
return nil, errors.Wrap(err)
}
statusInput, ok, err := approvedClientIntentsStatusToCloudFormat(clientIntents, intent)
if err != nil {
return nil, errors.Wrap(err)
}

input.Status = nil
if ok {
input.Status = statusInput
}
otterizeIntents = append(otterizeIntents, lo.ToPtr(input))
}
}

return otterizeIntents, nil
}

func approvedClientIntentsStatusToCloudFormat(approvedClientIntents ApprovedClientIntents, intent Target) (*graphqlclient.IntentStatusInput, bool, error) {
status := graphqlclient.IntentStatusInput{
IstioStatus: &graphqlclient.IstioStatusInput{},
}

serviceAccountName, ok := approvedClientIntents.Annotations[OtterizeClientServiceAccountAnnotation]
if !ok {
// Status is not set, nothing to do
return nil, false, nil
}

status.IstioStatus.ServiceAccountName = toPtrOrNil(serviceAccountName)
isSharedValue, ok := approvedClientIntents.Annotations[OtterizeSharedServiceAccountAnnotation]
isShared := false
if ok {
parsedIsShared, err := strconv.ParseBool(isSharedValue)
if err != nil {
return nil, false, errors.Errorf("failed to parse shared service account annotation for approved client intents %s", approvedClientIntents.Name)
}
isShared = parsedIsShared
}
status.IstioStatus.IsServiceAccountShared = lo.ToPtr(isShared)

clientMissingSidecarValue, ok := approvedClientIntents.Annotations[OtterizeMissingSidecarAnnotation]
clientMissingSidecar := false
if ok {
parsedClientMissingSidecar, err := strconv.ParseBool(clientMissingSidecarValue)
if err != nil {
return nil, false, errors.Errorf("failed to parse missing sidecar annotation for approved client intents %s", approvedClientIntents.Name)
}
clientMissingSidecar = parsedClientMissingSidecar
}
status.IstioStatus.IsClientMissingSidecar = lo.ToPtr(clientMissingSidecar)

isServerMissingSidecar, err := approvedClientIntents.IsServerMissingSidecar(intent)
if err != nil {
return nil, false, errors.Wrap(err)
}
status.IstioStatus.IsServerMissingSidecar = lo.ToPtr(isServerMissingSidecar)
return &status, true, nil
}

func (in *ApprovedClientIntents) IsServerMissingSidecar(intent Target) (bool, error) {
serversSet, err := in.GetServersWithoutSidecar()
if err != nil {
return false, errors.Wrap(err)
}
identity := intent.ToServiceIdentity(in.Namespace)
serverIdentity := identity.GetFormattedOtterizeIdentityWithoutKind()
return serversSet.Has(serverIdentity), nil
}
Loading