Skip to content

Commit 3c636ea

Browse files
Added WatchDeploymentLogsByLabelSelector function
1 parent df9c56c commit 3c636ea

File tree

3 files changed

+92
-32
lines changed

3 files changed

+92
-32
lines changed

docs/book/src/developer/providers/v1.3-to-v1.4.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,5 +35,6 @@ maintainers of providers and consumers of our Go API.
3535
### Other
3636

3737
- `clusterctl upgrade apply` no longer requires a namespace when updating providers. It is now optional and in a future release it will be deprecated. The new syntax is `[namespace/]provider:version`.
38+
- `WatchDeploymentLogs` is changed to `WatchDeploymentLogsByName`, it works same as before. Another function `WatchDeploymentLogsByLabelSelector` is added to stream logs of deployment by label selector.
3839

3940
### Suggested changes for providers

test/framework/clusterctl/clusterctl_helpers.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ func InitManagementClusterAndWatchControllerLogs(ctx context.Context, input Init
107107
}, intervals...)
108108

109109
// Start streaming logs from all controller providers
110-
framework.WatchDeploymentLogs(ctx, framework.WatchDeploymentLogsInput{
110+
framework.WatchDeploymentLogsByName(ctx, framework.WatchDeploymentLogsByNameInput{
111111
GetLister: client,
112112
ClientSet: input.ClusterProxy.GetClientSet(),
113113
Deployment: deployment,
@@ -188,7 +188,7 @@ func UpgradeManagementClusterAndWait(ctx context.Context, input UpgradeManagemen
188188
}, intervals...)
189189

190190
// Start streaming logs from all controller providers
191-
framework.WatchDeploymentLogs(ctx, framework.WatchDeploymentLogsInput{
191+
framework.WatchDeploymentLogsByName(ctx, framework.WatchDeploymentLogsByNameInput{
192192
GetLister: client,
193193
ClientSet: input.ClusterProxy.GetClientSet(),
194194
Deployment: deployment,

test/framework/deployment_helpers.go

Lines changed: 89 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -95,34 +95,56 @@ func DescribeFailedDeployment(input WaitForDeploymentsAvailableInput, deployment
9595
return b.String()
9696
}
9797

98-
// WatchDeploymentLogsInput is the input for WatchDeploymentLogs.
99-
type WatchDeploymentLogsInput struct {
98+
// WatchDeploymentLogsByLabelSelectorInput is the input for WatchDeploymentLogsByLabelSelector.
99+
type WatchDeploymentLogsByLabelSelectorInput struct {
100+
GetLister GetLister
101+
ClientSet *kubernetes.Clientset
102+
Labels map[string]string
103+
LogPath string
104+
}
105+
106+
// WatchDeploymentLogsByLabelSelector streams logs for all containers for all pods belonging to a deployment on the basis of label. Each container's logs are streamed
107+
// in a separate goroutine so they can all be streamed concurrently. This only causes a test failure if there are errors
108+
// retrieving the deployment, its pods, or setting up a log file. If there is an error with the log streaming itself,
109+
// that does not cause the test to fail.
110+
func WatchDeploymentLogsByLabelSelector(ctx context.Context, input WatchDeploymentLogsByLabelSelectorInput) {
111+
Expect(ctx).NotTo(BeNil(), "ctx is required for WatchDeploymentLogsByLabelSelector")
112+
Expect(input.ClientSet).NotTo(BeNil(), "input.ClientSet is required for WatchDeploymentLogsByLabelSelector")
113+
Expect(input.Labels).NotTo(BeNil(), "input.Selector is required for WatchDeploymentLogsByLabelSelector")
114+
115+
deploymentList := &appsv1.DeploymentList{}
116+
Eventually(func() error {
117+
return input.GetLister.List(ctx, deploymentList, client.MatchingLabels(input.Labels))
118+
}, retryableOperationTimeout, retryableOperationInterval).Should(Succeed(), "Failed to get deployment for labels")
119+
120+
for _, deployment := range deploymentList.Items {
121+
watchPodLogs(ctx, watchPodLogsInput{
122+
GetLister: input.GetLister,
123+
ClientSet: input.ClientSet,
124+
Namespace: deployment.Namespace,
125+
DeploymentName: deployment.Name,
126+
Labels: deployment.Spec.Selector.MatchLabels,
127+
LogPath: input.LogPath,
128+
})
129+
}
130+
}
131+
132+
// WatchDeploymentLogsByNameInput is the input for WatchDeploymentLogsByName.
133+
type WatchDeploymentLogsByNameInput struct {
100134
GetLister GetLister
101135
ClientSet *kubernetes.Clientset
102136
Deployment *appsv1.Deployment
103137
LogPath string
104138
}
105139

106-
// logMetadata contains metadata about the logs.
107-
// The format is very similar to the one used by promtail.
108-
type logMetadata struct {
109-
Job string `json:"job"`
110-
Namespace string `json:"namespace"`
111-
App string `json:"app"`
112-
Pod string `json:"pod"`
113-
Container string `json:"container"`
114-
NodeName string `json:"node_name"`
115-
Stream string `json:"stream"`
116-
}
117-
118-
// WatchDeploymentLogs streams logs for all containers for all pods belonging to a deployment. Each container's logs are streamed
140+
// WatchDeploymentLogsByName streams logs for all containers for all pods belonging to a deployment. Each container's logs are streamed
119141
// in a separate goroutine so they can all be streamed concurrently. This only causes a test failure if there are errors
120142
// retrieving the deployment, its pods, or setting up a log file. If there is an error with the log streaming itself,
121143
// that does not cause the test to fail.
122-
func WatchDeploymentLogs(ctx context.Context, input WatchDeploymentLogsInput) {
123-
Expect(ctx).NotTo(BeNil(), "ctx is required for WatchControllerLogs")
124-
Expect(input.ClientSet).NotTo(BeNil(), "input.ClientSet is required for WatchControllerLogs")
125-
Expect(input.Deployment).NotTo(BeNil(), "input.Deployment is required for WatchControllerLogs")
144+
func WatchDeploymentLogsByName(ctx context.Context, input WatchDeploymentLogsByNameInput) {
145+
Expect(ctx).NotTo(BeNil(), "ctx is required for WatchDeploymentLogsByName")
146+
Expect(input.ClientSet).NotTo(BeNil(), "input.ClientSet is required for WatchDeploymentLogsByName")
147+
Expect(input.Deployment).NotTo(BeNil(), "input.Deployment is required for WatchDeploymentLogsByName")
126148

127149
deployment := &appsv1.Deployment{}
128150
key := client.ObjectKeyFromObject(input.Deployment)
@@ -131,23 +153,47 @@ func WatchDeploymentLogs(ctx context.Context, input WatchDeploymentLogsInput) {
131153
}, retryableOperationTimeout, retryableOperationInterval).Should(Succeed(), "Failed to get deployment %s", klog.KObj(input.Deployment))
132154

133155
selector, err := metav1.LabelSelectorAsMap(deployment.Spec.Selector)
134-
Expect(err).NotTo(HaveOccurred(), "Failed to Pods selector for deployment %s", klog.KObj(input.Deployment))
156+
Expect(err).NotTo(HaveOccurred(), "Failed to create Pods selector for deployment %s", klog.KObj(input.Deployment))
157+
watchPodLogs(ctx, watchPodLogsInput{
158+
GetLister: input.GetLister,
159+
ClientSet: input.ClientSet,
160+
Namespace: deployment.Namespace,
161+
DeploymentName: deployment.Name,
162+
Labels: selector,
163+
LogPath: input.LogPath,
164+
})
165+
}
135166

167+
// watchPodLogsInput is the input for watchPodLogs.
168+
type watchPodLogsInput struct {
169+
GetLister GetLister
170+
ClientSet *kubernetes.Clientset
171+
Namespace string
172+
DeploymentName string
173+
Labels map[string]string
174+
LogPath string
175+
}
176+
177+
// watchPodLogs streams logs for all containers for all pods belonging to a deployment with the given label. Each container's logs are streamed
178+
// in a separate goroutine so they can all be streamed concurrently. This only causes a test failure if there are errors
179+
// retrieving the deployment, its pods, or setting up a log file. If there is an error with the log streaming itself,
180+
// that does not cause the test to fail.
181+
func watchPodLogs(ctx context.Context, input watchPodLogsInput) {
136182
pods := &corev1.PodList{}
137-
Expect(input.GetLister.List(ctx, pods, client.InNamespace(input.Deployment.Namespace), client.MatchingLabels(selector))).To(Succeed(), "Failed to list Pods for deployment %s", klog.KObj(input.Deployment))
183+
Expect(input.GetLister.List(ctx, pods, client.InNamespace(input.Namespace), client.MatchingLabels(input.Labels))).To(Succeed(), "Failed to list Pods for deployment %s", klog.KRef(input.Namespace, input.DeploymentName))
138184

139185
for _, pod := range pods.Items {
140-
for _, container := range deployment.Spec.Template.Spec.Containers {
141-
log.Logf("Creating log watcher for controller %s, pod %s, container %s", klog.KObj(input.Deployment), pod.Name, container.Name)
186+
for _, container := range pod.Spec.Containers {
187+
log.Logf("Creating log watcher for controller %s, pod %s, container %s", klog.KRef(input.Namespace, input.DeploymentName), pod.Name, container.Name)
142188

143189
// Create log metadata file.
144-
logMetadataFile := filepath.Clean(path.Join(input.LogPath, input.Deployment.Name, pod.Name, container.Name+"-log-metadata.json"))
190+
logMetadataFile := filepath.Clean(path.Join(input.LogPath, input.DeploymentName, pod.Name, container.Name+"-log-metadata.json"))
145191
Expect(os.MkdirAll(filepath.Dir(logMetadataFile), 0750)).To(Succeed())
146192

147193
metadata := logMetadata{
148-
Job: input.Deployment.Namespace + "/" + input.Deployment.Name,
149-
Namespace: input.Deployment.Namespace,
150-
App: input.Deployment.Name,
194+
Job: input.Namespace + "/" + input.DeploymentName,
195+
Namespace: input.Namespace,
196+
App: input.DeploymentName,
151197
Pod: pod.Name,
152198
Container: container.Name,
153199
NodeName: pod.Spec.NodeName,
@@ -161,7 +207,7 @@ func WatchDeploymentLogs(ctx context.Context, input WatchDeploymentLogsInput) {
161207
go func(pod corev1.Pod, container corev1.Container) {
162208
defer GinkgoRecover()
163209

164-
logFile := filepath.Clean(path.Join(input.LogPath, input.Deployment.Name, pod.Name, container.Name+".log"))
210+
logFile := filepath.Clean(path.Join(input.LogPath, input.DeploymentName, pod.Name, container.Name+".log"))
165211
Expect(os.MkdirAll(filepath.Dir(logFile), 0750)).To(Succeed())
166212

167213
f, err := os.OpenFile(logFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0600)
@@ -173,7 +219,7 @@ func WatchDeploymentLogs(ctx context.Context, input WatchDeploymentLogsInput) {
173219
Follow: true,
174220
}
175221

176-
podLogs, err := input.ClientSet.CoreV1().Pods(input.Deployment.Namespace).GetLogs(pod.Name, opts).Stream(ctx)
222+
podLogs, err := input.ClientSet.CoreV1().Pods(input.Namespace).GetLogs(pod.Name, opts).Stream(ctx)
177223
if err != nil {
178224
// Failing to stream logs should not cause the test to fail
179225
log.Logf("Error starting logs stream for pod %s, container %s: %v", klog.KRef(pod.Namespace, pod.Name), container.Name, err)
@@ -193,6 +239,19 @@ func WatchDeploymentLogs(ctx context.Context, input WatchDeploymentLogsInput) {
193239
}
194240
}
195241

242+
// logMetadata contains metadata about the logs.
243+
// The format is very similar to the one used by promtail.
244+
type logMetadata struct {
245+
Job string `json:"job"`
246+
Namespace string `json:"namespace"`
247+
App string `json:"app"`
248+
Pod string `json:"pod"`
249+
Container string `json:"container"`
250+
NodeName string `json:"node_name"`
251+
Stream string `json:"stream"`
252+
Labels map[string]string `json:"labels,omitempty"`
253+
}
254+
196255
type WatchPodMetricsInput struct {
197256
GetLister GetLister
198257
ClientSet *kubernetes.Clientset
@@ -215,7 +274,7 @@ func WatchPodMetrics(ctx context.Context, input WatchPodMetricsInput) {
215274
}, retryableOperationTimeout, retryableOperationInterval).Should(Succeed(), "Failed to get deployment %s", klog.KObj(input.Deployment))
216275

217276
selector, err := metav1.LabelSelectorAsMap(deployment.Spec.Selector)
218-
Expect(err).NotTo(HaveOccurred(), "Failed to Pods selector for deployment %s", klog.KObj(input.Deployment))
277+
Expect(err).NotTo(HaveOccurred(), "Failed to create Pods selector for deployment %s", klog.KObj(input.Deployment))
219278

220279
pods := &corev1.PodList{}
221280
Eventually(func() error {

0 commit comments

Comments
 (0)