Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
9e0dca6
env-setup: netbox and arista
denyost Apr 2, 2026
a2efe7c
remove unused kubernetes ressources for netbox
denyost Apr 2, 2026
3f4832a
init skeleton for target autodiscover implementation
denyost Apr 8, 2026
6650ab3
add temporary comment for netbox automation
denyost Apr 8, 2026
6d8ec08
update temporary comment
denyost Apr 8, 2026
072be72
Merge branch 'main' of https://github.com/gnmic/operator into dev/aut…
denyost Apr 8, 2026
c8c65bf
files linted
denyost Apr 9, 2026
7f67aa4
Merge branch 'main' of https://github.com/gnmic/operator into dev/aut…
denyost Apr 10, 2026
29857ff
moved netbox to its own makefile
denyost Apr 10, 2026
37febb1
makefile make netbox install in different kind clusters viable
denyost Apr 10, 2026
edcda75
added type for discovered targets
denyost Apr 10, 2026
fb43e40
added target loader interface
denyost Apr 10, 2026
e45f5ec
added loader registry for dynamic target loader creation
denyost Apr 10, 2026
e288283
added target_manager for target lifecycle management
denyost Apr 10, 2026
ce5f701
wire target_manager and loader together
denyost Apr 10, 2026
bee64d7
add scheme back to the reconciler
denyost Apr 10, 2026
09fd1ef
add example return output for a poc
denyost Apr 13, 2026
5eb0088
comment templated code
denyost Apr 13, 2026
91f4985
register loaders
denyost Apr 13, 2026
3e1620a
makefile support for applying targetsources
denyost Apr 13, 2026
9fe3b10
add type for targetsourcespec
denyost Apr 13, 2026
8ab89e2
determine targetsource loader based on spec
denyost Apr 13, 2026
ccaffc1
add type for custom TargetSource CR
denyost Apr 13, 2026
d46368c
Merge branch 'main' of https://github.com/gnmic/operator into feature…
denyost Apr 13, 2026
d7750dc
changed NewLoader function call
mcdillson Apr 13, 2026
4cab2ba
added discovery message to types
mcdillson Apr 13, 2026
6981316
changed target source channel type
mcdillson Apr 13, 2026
be4514a
fixed http_pull implementation based on new types
mcdillson Apr 13, 2026
47b9342
changed struct layout to use provider section and made fields obligatory
mcdillson Apr 14, 2026
5ccface
restructured project to introduce new architecture
mcdillson Apr 15, 2026
c2a9245
renamed targetsource package to discovery
mcdillson Apr 15, 2026
f951046
fixed code after semi-merge
mcdillson Apr 15, 2026
d79dff1
regenerated manifests
mcdillson Apr 15, 2026
cc0be6e
update test targetsource according to the new definition
denyost Apr 16, 2026
78fd8b2
pass TargetSourceSpec to loader
denyost Apr 16, 2026
9a85123
handle TargetSource deletion
denyost Apr 16, 2026
7a94e3f
exit reconciliation after handling the CR deletion
denyost Apr 16, 2026
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ Dockerfile.cross
*~
private/
lab/dev/clab-*
lab/dev/netbox/secrets
design/
notes/
docs/public
Expand Down
14 changes: 12 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
include test.mk
include netbox.mk

# Image URL to use all building/pushing image targets
IMG ?= controller:latest
Expand Down Expand Up @@ -250,10 +251,10 @@ configure-nodes-dev-lab: ## Configure the nodes in the development lab cluster
gnmic -a clab-3-nodes-leaf2:57400 -u $(TARGET_USERNAME) -p $(TARGET_PASSWORD) --skip-verify set --request-file lab/dev/configs/leaf2.yaml

.PHONY: apply-resources-dev-lab
apply-resources-dev-lab: apply-targets-dev-lab apply-subscriptions-dev-lab apply-outputs-dev-lab apply-pipelines-dev-lab apply-clusters-dev-lab ## Apply the resources for the development lab cluster
apply-resources-dev-lab: apply-targets-dev-lab apply-subscriptions-dev-lab apply-outputs-dev-lab apply-pipelines-dev-lab apply-clusters-dev-lab apply-targetsources-dev-lab ## Apply the resources for the development lab cluster

.PHONY: delete-resources-dev-lab
delete-resources-dev-lab: delete-clusters-dev-lab delete-targets-dev-lab delete-subscriptions-dev-lab delete-outputs-dev-lab delete-pipelines-dev-lab ## Delete the resources for the development lab cluster
delete-resources-dev-lab: delete-clusters-dev-lab delete-targets-dev-lab delete-subscriptions-dev-lab delete-outputs-dev-lab delete-pipelines-dev-lab delete-targetsources-dev-lab ## Delete the resources for the development lab cluster

.PHONY: apply-targets-dev-lab
apply-targets-dev-lab: ## Apply the targets for the development lab cluster
Expand Down Expand Up @@ -287,6 +288,7 @@ apply-pipelines-dev-lab: ## Apply the pipelines for the development lab cluster
.PHONY: delete-pipelines-dev-lab
delete-pipelines-dev-lab: ## Delete the pipelines for the development lab cluster
kubectl delete -f lab/dev/resources/pipelines

.PHONY: apply-clusters-dev-lab
apply-clusters-dev-lab: ## Apply the clusters for the development lab cluster
kubectl apply -f lab/dev/resources/clusters
Expand All @@ -295,6 +297,14 @@ apply-clusters-dev-lab: ## Apply the clusters for the development lab cluster
delete-clusters-dev-lab: ## Delete the clusters for the development lab cluster
kubectl delete -f lab/dev/resources/clusters

.PHONY: apply-targetsources-dev-lab
apply-targetsources-dev-lab: ## Apply the target sources for the development lab cluster
kubectl apply -f lab/dev/resources/targetsources

.PHONY: delete-targetsources-dev-lab
delete-targetsources-dev-lab: ## Delete the target sources for the development lab cluster
kubectl delete -f lab/dev/resources/targetsources

##@ Testing Lab

.PHONY: run-integration-tests
Expand Down
17 changes: 12 additions & 5 deletions api/v1alpha1/targetsource_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,21 @@ import (
)

// TargetSourceSpec defines the desired state of TargetSource
// +kubebuilder:validation:Required
type TargetSourceSpec struct {
HTTP *HTTPConfig `json:"http,omitempty"`
Consul *ConsulConfig `json:"consul,omitempty"`
ConfigMap string `json:"configMap,omitempty"`
PodSelector metav1.LabelSelector `json:"podSelector,omitempty"`
ServiceSelector metav1.LabelSelector `json:"serviceSelector,omitempty"`
Provider *ProviderSpec `json:"provider"`
//
Type string `json:"type,omitempty"`
Labels map[string]string `json:"labels,omitempty"`

// +kubebuilder:validation:MinLength=1
Profile string `json:"profile"`
}

// +kubebuilder:validation:MaxProperties=1
type ProviderSpec struct {
HTTP *HTTPConfig `json:"http,omitempty"`
Consul *ConsulConfig `json:"consul,omitempty"`
}

type HTTPConfig struct {
Expand Down
40 changes: 29 additions & 11 deletions api/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

124 changes: 18 additions & 106 deletions config/crd/bases/operator.gnmic.dev_targetsources.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -39,120 +39,32 @@ spec:
spec:
description: TargetSourceSpec defines the desired state of TargetSource
properties:
configMap:
type: string
consul:
properties:
url:
type: string
type: object
http:
properties:
url:
type: string
type: object
labels:
additionalProperties:
type: string
type: object
podSelector:
description: |-
A label selector is a label query over a set of resources. The result of matchLabels and
matchExpressions are ANDed. An empty label selector matches all objects. A null
label selector matches no objects.
profile:
minLength: 1
type: string
provider:
maxProperties: 1
properties:
matchExpressions:
description: matchExpressions is a list of label selector requirements.
The requirements are ANDed.
items:
description: |-
A label selector requirement is a selector that contains values, a key, and an operator that
relates the key and values.
properties:
key:
description: key is the label key that the selector applies
to.
type: string
operator:
description: |-
operator represents a key's relationship to a set of values.
Valid operators are In, NotIn, Exists and DoesNotExist.
type: string
values:
description: |-
values is an array of string values. If the operator is In or NotIn,
the values array must be non-empty. If the operator is Exists or DoesNotExist,
the values array must be empty. This array is replaced during a strategic
merge patch.
items:
type: string
type: array
x-kubernetes-list-type: atomic
required:
- key
- operator
type: object
type: array
x-kubernetes-list-type: atomic
matchLabels:
additionalProperties:
type: string
description: |-
matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels
map is equivalent to an element of matchExpressions, whose key field is "key", the
operator is "In", and the values array contains only "value". The requirements are ANDed.
consul:
properties:
url:
type: string
type: object
type: object
x-kubernetes-map-type: atomic
serviceSelector:
description: |-
A label selector is a label query over a set of resources. The result of matchLabels and
matchExpressions are ANDed. An empty label selector matches all objects. A null
label selector matches no objects.
properties:
matchExpressions:
description: matchExpressions is a list of label selector requirements.
The requirements are ANDed.
items:
description: |-
A label selector requirement is a selector that contains values, a key, and an operator that
relates the key and values.
properties:
key:
description: key is the label key that the selector applies
to.
type: string
operator:
description: |-
operator represents a key's relationship to a set of values.
Valid operators are In, NotIn, Exists and DoesNotExist.
type: string
values:
description: |-
values is an array of string values. If the operator is In or NotIn,
the values array must be non-empty. If the operator is Exists or DoesNotExist,
the values array must be empty. This array is replaced during a strategic
merge patch.
items:
type: string
type: array
x-kubernetes-list-type: atomic
required:
- key
- operator
type: object
type: array
x-kubernetes-list-type: atomic
matchLabels:
additionalProperties:
type: string
description: |-
matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels
map is equivalent to an element of matchExpressions, whose key field is "key", the
operator is "In", and the values array contains only "value". The requirements are ANDed.
http:
properties:
url:
type: string
type: object
type: object
x-kubernetes-map-type: atomic
type:
type: string
required:
- profile
- provider
type: object
status:
description: TargetSourceStatus defines the observed state of TargetSource
Expand Down
27 changes: 27 additions & 0 deletions internal/controller/discovery/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package discovery

// File may become obsolete, depends on how the logic to compare desired vs. existing state will get implemented

import (
"context"

"sigs.k8s.io/controller-runtime/pkg/client"

gnmicv1alpha1 "github.com/gnmic/operator/api/v1alpha1"
)

func FetchExistingTargets(ctx context.Context, c client.Client, ts gnmicv1alpha1.TargetSource) ([]gnmicv1alpha1.Target, error) {
var targetList gnmicv1alpha1.TargetList

err := c.List(ctx, &targetList,
client.InNamespace(ts.Namespace),
client.MatchingLabels{
"gnmic.io/source": ts.Name,
},
)
if err != nil {
return nil, err
}

return targetList.Items, nil
}
23 changes: 23 additions & 0 deletions internal/controller/discovery/core/loader_interface.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package core

import (
"context"

gnmicv1alpha1 "github.com/gnmic/operator/api/v1alpha1"
)

// Loader defines a pluggable TargetSource loader interface
// Loaders observe external Sources of Truth and emit target snapshots through a channel
type Loader interface {
// Name returns the unique loader identifier e.g. "http_pull"
Name() string

// Start begins discovery and pushes target snapshots into the out channel
// The loader must stop cleanly when ctx is cancelled
Start(
ctx context.Context,
targetsourceName string,
spec gnmicv1alpha1.TargetSourceSpec,
out chan<- []DiscoveryMessage,
) error
}
22 changes: 22 additions & 0 deletions internal/controller/discovery/core/types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package core

// DiscoveredTarget represents a target discovered from an external source
// before it is materialized as a Kubernetes Target CR
type DiscoveredTarget struct {
Name string
Address string
Labels map[string]string
}

const (
DELETE DiscoveryEvent = 0
CREATE DiscoveryEvent = 1
UPDATE DiscoveryEvent = 2
)

type DiscoveryEvent int

type DiscoveryMessage struct {
Target DiscoveredTarget
Event DiscoveryEvent
}
24 changes: 24 additions & 0 deletions internal/controller/discovery/loader.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package discovery

import (
"fmt"

gnmicv1alpha1 "github.com/gnmic/operator/api/v1alpha1"
"github.com/gnmic/operator/internal/controller/discovery/core"
"github.com/gnmic/operator/internal/controller/discovery/loaders/http_pull"
)

// NewLoader creates a loader by name
func NewLoader(name string, namespace string, spec gnmicv1alpha1.TargetSourceSpec) (core.Loader, error) {
loaderName := namespace + "/" + name

switch {
case spec.Provider.HTTP != nil:
return http_pull.New(), nil
case spec.Provider.Consul != nil:
return nil, fmt.Errorf("unknown targetsource loader, check TargetSource CRD for %s", loaderName)
default:
return nil, fmt.Errorf("unknown targetsource loader, check TargetSource CRD for %s", loaderName)
}

}
6 changes: 6 additions & 0 deletions internal/controller/discovery/loaders/all/all.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package all

import (
_ "github.com/gnmic/operator/internal/controller/discovery/loaders/http_pull"
// _ "github.com/gnmic/operator/internal/controller/targetsource/loaders/http_push"
)
Loading
Loading