Skip to content

Commit e0e5941

Browse files
authored
Merge pull request #61 from ctrlplanedev/mleonidas/add-node-sync-and-selector-arg
feat: sync nodes and add --selector arg
2 parents 186efd6 + d8b4c7b commit e0e5941

4 files changed

Lines changed: 305 additions & 119 deletions

File tree

Lines changed: 9 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -1,75 +1,21 @@
11
package kubernetes
22

33
import (
4-
"context"
54
"fmt"
6-
"strings"
75

86
"github.com/MakeNowJust/heredoc/v2"
97
"github.com/charmbracelet/log"
108
"github.com/ctrlplanedev/cli/internal/api"
119
ctrlp "github.com/ctrlplanedev/cli/internal/common"
1210
"github.com/spf13/cobra"
1311
"github.com/spf13/viper"
14-
appsv1 "k8s.io/api/apps/v1"
15-
corev1 "k8s.io/api/core/v1"
16-
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
17-
"k8s.io/client-go/kubernetes"
1812
)
1913

20-
func processNamespace(_ context.Context, clusterName string, namespace corev1.Namespace) api.ResourceProviderResource {
21-
metadata := map[string]string{}
22-
for key, value := range namespace.Labels {
23-
metadata[fmt.Sprintf("tags/%s", key)] = value
24-
}
25-
26-
metadata["kubernetes/namespace"] = namespace.Name
27-
metadata["namespace/id"] = string(namespace.UID)
28-
metadata["namespace/api-version"] = namespace.APIVersion
29-
metadata["namespace/status"] = string(namespace.Status.Phase)
30-
31-
return api.ResourceProviderResource{
32-
Version: "ctrlplane.dev/kubernetes/namespace/v1",
33-
Kind: "KubernetesNamespace",
34-
Name: fmt.Sprintf("%s/%s", clusterName, namespace.Name),
35-
Identifier: string(namespace.UID),
36-
Config: map[string]any{
37-
"id": string(namespace.UID),
38-
"name": namespace.Name,
39-
"status": namespace.Status.Phase,
40-
},
41-
Metadata: metadata,
42-
}
43-
}
44-
45-
func processDeployment(_ context.Context, clusterName string, deployment appsv1.Deployment) api.ResourceProviderResource {
46-
metadata := map[string]string{}
47-
for key, value := range deployment.Labels {
48-
metadata[fmt.Sprintf("tags/%s", key)] = value
49-
}
50-
metadata["deployment/name"] = deployment.Name
51-
metadata["deployment/id"] = string(deployment.UID)
52-
metadata["deployment/api-version"] = deployment.APIVersion
53-
metadata["deployment/namespace"] = deployment.Namespace
54-
55-
return api.ResourceProviderResource{
56-
Version: "ctrlplane.dev/kubernetes/deployment/v1",
57-
Kind: "KubernetesDeployment",
58-
Name: fmt.Sprintf("%s/%s/%s", clusterName, deployment.Namespace, deployment.Name),
59-
Identifier: string(deployment.UID),
60-
Config: map[string]any{
61-
"id": string(deployment.UID),
62-
"name": deployment.Name,
63-
"namespace": deployment.Namespace,
64-
},
65-
Metadata: metadata,
66-
}
67-
}
68-
6914
func NewSyncKubernetesCmd() *cobra.Command {
7015
var clusterIdentifier string
7116
var providerName string
7217
var clusterName string
18+
var selectors ResourceTypes
7319

7420
cmd := &cobra.Command{
7521
Use: "kubernetes",
@@ -78,7 +24,7 @@ func NewSyncKubernetesCmd() *cobra.Command {
7824
$ ctrlc sync kubernetes --cluster-identifier 1234567890 --cluster-name my-cluster
7925
`),
8026
RunE: func(cmd *cobra.Command, args []string) error {
81-
ctx := context.Background()
27+
ctx := cmd.Context()
8228
log.Info("Syncing Kubernetes resources on a cluster")
8329
if clusterIdentifier == "" {
8430
clusterIdentifier = viper.GetString("cluster-identifier")
@@ -89,6 +35,10 @@ func NewSyncKubernetesCmd() *cobra.Command {
8935
return err
9036
}
9137

38+
if clusterName == "" {
39+
clusterName = configClusterName
40+
}
41+
9242
log.Info("Connected to cluster", "name", clusterName)
9343

9444
apiURL := viper.GetString("url")
@@ -99,70 +49,19 @@ func NewSyncKubernetesCmd() *cobra.Command {
9949
if err != nil {
10050
return fmt.Errorf("failed to create API client: %w", err)
10151
}
102-
103-
clusterResource, err := ctrlplaneClient.GetResourceByIdentifierWithResponse(ctx, workspaceId, clusterIdentifier)
104-
if err != nil {
105-
log.Warn("Failed to get cluster resource")
106-
}
107-
if clusterResource.StatusCode() > 499 {
108-
log.Warn("Failed to get cluster resource", "status", clusterResource.StatusCode(), "identifier", clusterIdentifier, "error", err)
109-
return fmt.Errorf("error access ctrlplane api: %s", clusterResource.Status())
110-
}
111-
if clusterResource != nil && clusterResource.JSON200 != nil {
112-
log.Info("Found cluster resource", "name", clusterResource.JSON200.Name)
113-
clusterName = clusterResource.JSON200.Name
114-
}
115-
116-
if clusterName == "" {
117-
clusterName = configClusterName
118-
}
119-
120-
clientset, err := kubernetes.NewForConfig(config)
121-
if err != nil {
122-
return err
123-
}
124-
125-
namespaces, err := clientset.CoreV1().Namespaces().List(ctx, metav1.ListOptions{})
52+
sync := newSync(clusterIdentifier, workspaceId, ctrlplaneClient, config, clusterName)
53+
resources, err := sync.process(ctx, selectors)
12654
if err != nil {
12755
return err
12856
}
12957

130-
resources := []api.ResourceProviderResource{}
131-
for _, namespace := range namespaces.Items {
132-
resource := processNamespace(context.Background(), clusterName, namespace)
133-
resources = append(resources, resource)
134-
}
135-
136-
deployments, err := clientset.AppsV1().Deployments(metav1.NamespaceAll).List(ctx, metav1.ListOptions{})
137-
if err != nil {
138-
return err
139-
}
140-
141-
for _, deployment := range deployments.Items {
142-
resource := processDeployment(context.Background(), clusterName, deployment)
143-
resources = append(resources, resource)
144-
}
145-
146-
if clusterResource != nil && clusterResource.JSON200 != nil {
147-
for _, resource := range resources {
148-
for key, value := range clusterResource.JSON200.Metadata {
149-
if strings.HasPrefix(key, "tags/") {
150-
continue
151-
}
152-
if _, exists := resource.Metadata[key]; !exists {
153-
resource.Metadata[key] = value
154-
}
155-
}
156-
resource.Metadata["kubernetes/name"] = clusterResource.JSON200.Name
157-
}
158-
}
159-
16058
return ctrlp.UpsertResources(ctx, resources, &providerName)
16159
},
16260
}
16361
cmd.Flags().StringVarP(&providerName, "provider", "p", "", "Name of the resource provider")
16462
cmd.Flags().StringVarP(&clusterIdentifier, "cluster-identifier", "c", "", "The identifier of the parent cluster in ctrlplane (if not provided, will use the CLUSTER_IDENTIFIER environment variable)")
16563
cmd.Flags().StringVarP(&clusterName, "cluster-name", "n", "", "The name of the cluster")
64+
cmd.Flags().VarP(&selectors, "selector", "s", "Select resources to sync [nodes|deployments|namespaces]. Repeat the flag to select multiple resources; omit it to sync all resources.")
16665

16766
return cmd
16867
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package kubernetes
2+
3+
import (
4+
"fmt"
5+
"slices"
6+
"strings"
7+
)
8+
9+
type ResourceType string
10+
11+
const (
12+
ResourceNamespace ResourceType = "namespaces"
13+
ResourceNode ResourceType = "nodes"
14+
ResourceDeployment ResourceType = "deployments"
15+
)
16+
17+
func (r ResourceType) String() string {
18+
return string(r)
19+
}
20+
21+
func ParseResourceType(s string) (ResourceType, error) {
22+
switch ResourceType(s) {
23+
case ResourceNamespace, ResourceNode, ResourceDeployment:
24+
return ResourceType(s), nil
25+
default:
26+
return "", fmt.Errorf("invalid resource type %q", s)
27+
}
28+
}
29+
30+
type ResourceTypes []ResourceType
31+
32+
func (r ResourceTypes) ShouldFetch(target ResourceType) bool {
33+
return slices.Contains(r, target) || len(r) == 0
34+
}
35+
36+
func (r *ResourceTypes) String() string {
37+
if r == nil {
38+
return ""
39+
}
40+
out := make([]string, len(*r))
41+
for i, v := range *r {
42+
out[i] = v.String()
43+
}
44+
return strings.Join(out, ",")
45+
}
46+
47+
func (s *ResourceTypes) Type() string {
48+
return "resourceType"
49+
}
50+
51+
func (r *ResourceTypes) Set(value string) error {
52+
// supports repeated flags like:
53+
// --resource namespace --resource node
54+
rt, err := ParseResourceType(value)
55+
if err != nil {
56+
return err
57+
}
58+
*r = append(*r, rt)
59+
return nil
60+
}

0 commit comments

Comments
 (0)