• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

NVIDIA / skyhook / 20174212535

12 Dec 2025 05:08PM UTC coverage: 76.44%. First build
20174212535

Pull #127

github

t0mmylam
feat(cli): Add lifecycle management commands (pause, resume, disable, enable)
Pull Request #127: feat(cli): Add lifecycle management commands (pause, resume, disable, enable)

1159 of 1462 new or added lines in 17 files covered. (79.27%)

5798 of 7585 relevant lines covered (76.44%)

0.87 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

52.78
/operator/internal/cli/utils/utils.go
1
/*
2
 * SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3
 * SPDX-License-Identifier: Apache-2.0
4
 *
5
 *
6
 * Licensed under the Apache License, Version 2.0 (the "License");
7
 * you may not use this file except in compliance with the License.
8
 * You may obtain a copy of the License at
9
 *
10
 * http://www.apache.org/licenses/LICENSE-2.0
11
 *
12
 * Unless required by applicable law or agreed to in writing, software
13
 * distributed under the License is distributed on an "AS IS" BASIS,
14
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
 * See the License for the specific language governing permissions and
16
 * limitations under the License.
17
 */
18

19
package utils
20

21
import (
22
        "context"
23
        "encoding/json"
24
        "fmt"
25
        "regexp"
26
        "strings"
27

28
        metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
29
        "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
30
        "k8s.io/apimachinery/pkg/types"
31
        "k8s.io/client-go/dynamic"
32

33
        "github.com/NVIDIA/skyhook/operator/api/v1alpha1"
34
)
35

36
// MatchNodes matches node patterns against a list of available nodes.
37
// Patterns can be exact node names or regex patterns.
38
func MatchNodes(patterns []string, availableNodes []string) ([]string, error) {
1✔
39
        matched := make(map[string]bool)
1✔
40

1✔
41
        for _, pattern := range patterns {
2✔
42
                // Check if it's a regex pattern (contains regex metacharacters)
1✔
43
                isRegex := strings.ContainsAny(pattern, ".*+?^${}[]|()\\")
1✔
44

1✔
45
                if isRegex {
2✔
46
                        re, err := regexp.Compile("^" + pattern + "$")
1✔
47
                        if err != nil {
2✔
48
                                return nil, fmt.Errorf("invalid regex pattern %q: %w", pattern, err)
1✔
49
                        }
1✔
50

51
                        for _, node := range availableNodes {
2✔
52
                                if re.MatchString(node) {
2✔
53
                                        matched[node] = true
1✔
54
                                }
1✔
55
                        }
56
                } else {
1✔
57
                        // Exact match
1✔
58
                        for _, node := range availableNodes {
2✔
59
                                if node == pattern {
2✔
60
                                        matched[node] = true
1✔
61
                                }
1✔
62
                        }
63
                }
64
        }
65

66
        result := make([]string, 0, len(matched))
1✔
67
        for node := range matched {
2✔
68
                result = append(result, node)
1✔
69
        }
1✔
70

71
        return result, nil
1✔
72
}
73

74
// EscapeJSONPointer escapes special characters in JSON Pointer tokens
75
// per RFC 6901: ~ becomes ~0, / becomes ~1
76
func EscapeJSONPointer(s string) string {
1✔
77
        s = strings.ReplaceAll(s, "~", "~0")
1✔
78
        s = strings.ReplaceAll(s, "/", "~1")
1✔
79
        return s
1✔
80
}
1✔
81

82
// UnstructuredToSkyhook converts an unstructured object to a Skyhook.
83
func UnstructuredToSkyhook(u *unstructured.Unstructured) (*v1alpha1.Skyhook, error) {
1✔
84
        data, err := u.MarshalJSON()
1✔
85
        if err != nil {
1✔
NEW
86
                return nil, fmt.Errorf("marshaling unstructured: %w", err)
×
NEW
87
        }
×
88

89
        var skyhook v1alpha1.Skyhook
1✔
90
        if err := json.Unmarshal(data, &skyhook); err != nil {
1✔
NEW
91
                return nil, fmt.Errorf("unmarshaling to skyhook: %w", err)
×
NEW
92
        }
×
93

94
        return &skyhook, nil
1✔
95
}
96

97
// Skyhook annotation keys
98
const (
99
        PauseAnnotation   = v1alpha1.METADATA_PREFIX + "/pause"
100
        DisableAnnotation = v1alpha1.METADATA_PREFIX + "/disable"
101
)
102

103
// SetSkyhookAnnotation sets an annotation on a Skyhook CR using dynamic client
104
// Note: Skyhook is a cluster-scoped resource (not namespaced)
NEW
105
func SetSkyhookAnnotation(ctx context.Context, dynamicClient dynamic.Interface, skyhookName, annotation, value string) error {
×
NEW
106
        patch := fmt.Sprintf(`{"metadata":{"annotations":{%q:%q}}}`, annotation, value)
×
NEW
107

×
NEW
108
        gvr := v1alpha1.GroupVersion.WithResource("skyhooks")
×
NEW
109
        _, err := dynamicClient.Resource(gvr).Patch(
×
NEW
110
                ctx,
×
NEW
111
                skyhookName,
×
NEW
112
                types.MergePatchType,
×
NEW
113
                []byte(patch),
×
NEW
114
                metav1.PatchOptions{},
×
NEW
115
        )
×
NEW
116
        if err != nil {
×
NEW
117
                return fmt.Errorf("patching skyhook %q: %w", skyhookName, err)
×
NEW
118
        }
×
119

NEW
120
        return nil
×
121
}
122

123
// RemoveSkyhookAnnotation removes an annotation from a Skyhook CR using dynamic client
124
// Note: Skyhook is a cluster-scoped resource (not namespaced)
NEW
125
func RemoveSkyhookAnnotation(ctx context.Context, dynamicClient dynamic.Interface, skyhookName, annotation string) error {
×
NEW
126
        patch := fmt.Sprintf(`{"metadata":{"annotations":{%q:null}}}`, annotation)
×
NEW
127

×
NEW
128
        gvr := v1alpha1.GroupVersion.WithResource("skyhooks")
×
NEW
129
        _, err := dynamicClient.Resource(gvr).Patch(
×
NEW
130
                ctx,
×
NEW
131
                skyhookName,
×
NEW
132
                types.MergePatchType,
×
NEW
133
                []byte(patch),
×
NEW
134
                metav1.PatchOptions{},
×
NEW
135
        )
×
NEW
136
        if err != nil {
×
NEW
137
                return fmt.Errorf("patching skyhook %q: %w", skyhookName, err)
×
NEW
138
        }
×
139

NEW
140
        return nil
×
141
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc