predicate.go 4.19 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/*
 * SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
 * SPDX-License-Identifier: Apache-2.0
 *
 * 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 controller_common

import (
	"context"
	"strings"
23
	"time"
24
25

	"k8s.io/apimachinery/pkg/api/meta"
26
27
	"k8s.io/client-go/discovery"
	ctrl "sigs.k8s.io/controller-runtime"
28
29
30
31
32
	"sigs.k8s.io/controller-runtime/pkg/client"
	"sigs.k8s.io/controller-runtime/pkg/log"
	"sigs.k8s.io/controller-runtime/pkg/predicate"
)

33
34
35
36
37
38
39
type GroveConfig struct {
	// Enabled is automatically determined by checking if Grove CRDs are installed in the cluster
	Enabled bool
	// TerminationDelay configures the termination delay for Grove PodGangSets
	TerminationDelay time.Duration
}

40
41
42
43
44
type LWSConfig struct {
	// Enabled is automatically determined by checking if LWS CRDs are installed in the cluster
	Enabled bool
}

45
46
47
type Config struct {
	// Enable resources filtering, only the resources belonging to the given namespace will be handled.
	RestrictedNamespace string
48
	Grove               GroveConfig
49
	LWS                 LWSConfig
50
51
52
53
54
55
56
57
58
59
60
61
62
63
	EtcdAddress         string
	NatsAddress         string
	IngressConfig       IngressConfig
}

type IngressConfig struct {
	VirtualServiceGateway      string
	IngressControllerClassName string
	IngressControllerTLSSecret string
	IngressHostSuffix          string
}

func (i *IngressConfig) UseVirtualService() bool {
	return i.VirtualServiceGateway != ""
64
65
}

66
67
68
// DetectGroveAvailability checks if Grove is available by checking if the Grove API group is registered
// This approach uses the discovery client which is simpler and more reliable
func DetectGroveAvailability(ctx context.Context, mgr ctrl.Manager) bool {
69
70
71
72
73
74
75
76
77
78
79
	return detectAPIGroupAvailability(ctx, mgr, "grove.io")
}

// DetectLWSAvailability checks if LWS is available by checking if the LWS API group is registered
// This approach uses the discovery client which is simpler and more reliable
func DetectLWSAvailability(ctx context.Context, mgr ctrl.Manager) bool {
	return detectAPIGroupAvailability(ctx, mgr, "leaderworkerset.x-k8s.io")
}

// detectAPIGroupAvailability checks if a specific API group is registered in the cluster
func detectAPIGroupAvailability(ctx context.Context, mgr ctrl.Manager, groupName string) bool {
80
81
82
83
	logger := log.FromContext(ctx)

	cfg := mgr.GetConfig()
	if cfg == nil {
84
		logger.Info("detection failed, no discovery client available", "group", groupName)
85
86
87
88
89
		return false
	}

	discoveryClient, err := discovery.NewDiscoveryClientForConfig(cfg)
	if err != nil {
90
		logger.Error(err, "detection failed, could not create discovery client", "group", groupName)
91
92
93
94
95
		return false
	}

	apiGroups, err := discoveryClient.ServerGroups()
	if err != nil {
96
		logger.Error(err, "detection failed, could not list server groups", "group", groupName)
97
98
99
100
		return false
	}

	for _, group := range apiGroups.Groups {
101
102
		if group.Name == groupName {
			logger.Info("API group is available", "group", groupName)
103
104
105
106
			return true
		}
	}

107
	logger.Info("API group not available", "group", groupName)
108
109
	return false
}
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
func EphemeralDeploymentEventFilter(config Config) predicate.Predicate {
	return predicate.NewPredicateFuncs(func(o client.Object) bool {
		l := log.FromContext(context.Background())
		objMeta, err := meta.Accessor(o)
		if err != nil {
			l.Error(err, "Error extracting object metadata")
			return false
		}
		if config.RestrictedNamespace != "" {
			// in case of a restricted namespace, we only want to process the events that are in the restricted namespace
			return objMeta.GetNamespace() == config.RestrictedNamespace
		}
		// in all other cases, discard the event if it is destined to an ephemeral deployment
		if strings.Contains(objMeta.GetNamespace(), "ephemeral") {
			return false
		}
		return true
	})
}