Skip to content

Commit d4a9f3e

Browse files
jpinsonneaujotak
andauthored
NETOBSERV-1907 run in background (#102)
* run in background * install tar if needed * fix maxTime log * fix option arg * Update commands/netobserv Co-authored-by: Joel Takvorian <[email protected]> * improve background message --------- Co-authored-by: Joel Takvorian <[email protected]>
1 parent 1372c65 commit d4a9f3e

File tree

9 files changed

+209
-49
lines changed

9 files changed

+209
-49
lines changed

Dockerfile

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,14 @@ COPY res/ res/
2424
COPY scripts/ scripts/
2525
COPY Makefile Makefile
2626
COPY .mk/ .mk/
27-
# Embed commands in case users want to pull it from collector image
27+
28+
# Install oc to allow collector to run commands
29+
RUN set -x; \
30+
OC_TAR_URL="https://mirror.openshift.com/pub/openshift-v4/$(uname -m)/clients/ocp/latest/openshift-client-linux.tar.gz" && \
31+
curl -L -q -o /tmp/oc.tar.gz "$OC_TAR_URL" && \
32+
tar -C /tmp -xvf /tmp/oc.tar.gz oc kubectl
33+
34+
# Embedd commands in case users want to pull it from collector image
2835
RUN USER=netobserv VERSION=main make oc-commands
2936

3037
# Prepare output dir
@@ -33,7 +40,11 @@ RUN mkdir -p output
3340
# Create final image from ubi + built binary and command
3441
FROM --platform=linux/$TARGETARCH registry.access.redhat.com/ubi9/ubi:9.4
3542
WORKDIR /
43+
44+
COPY --from=builder /opt/app-root/build .
3645
COPY --from=builder /opt/app-root/build .
46+
COPY --from=builder /tmp/oc /usr/bin/oc
47+
COPY --from=builder /tmp/kubectl /usr/bin/kubectl
3748
COPY --from=builder --chown=65532:65532 /opt/app-root/output /output
3849
USER 65532:65532
3950

cmd/flow_capture.go

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -148,16 +148,20 @@ func runFlowCaptureOnAddr(port int, filename string) {
148148
// terminate capture if max bytes reached
149149
totalBytes = totalBytes + int64(bytes)
150150
if totalBytes > maxBytes {
151-
log.Infof("Capture reached %s, exiting now...", sizestr.ToString(maxBytes))
152-
return
151+
if exit := onLimitReached(); exit {
152+
log.Infof("Capture reached %s, exiting now...", sizestr.ToString(maxBytes))
153+
return
154+
}
153155
}
154156

155157
// terminate capture if max time reached
156158
now := currentTime()
157159
duration := now.Sub(startupTime)
158160
if int(duration) > int(maxTime) {
159-
log.Infof("Capture reached %s, exiting now...", maxTime)
160-
return
161+
if exit := onLimitReached(); exit {
162+
log.Infof("Capture reached %s, exiting now...", maxTime)
163+
return
164+
}
161165
}
162166

163167
captureStarted = true
@@ -242,7 +246,7 @@ func toSize(fieldName string) int {
242246
func updateTable() {
243247
// don't refresh terminal too often to avoid blinking
244248
now := currentTime()
245-
if int(now.Sub(lastRefresh)) > int(maxRefreshRate) {
249+
if !captureEnded && int(now.Sub(lastRefresh)) > int(maxRefreshRate) {
246250
lastRefresh = now
247251
resetTerminal()
248252

@@ -252,12 +256,18 @@ func updateTable() {
252256
fmt.Printf("Log level: %s ", logLevel)
253257
fmt.Printf("Duration: %s ", duration.Round(time.Second))
254258
fmt.Printf("Capture size: %s\n", sizestr.ToString(totalBytes))
255-
if len(strings.TrimSpace(filter)) > 0 {
256-
fmt.Printf("Filters: %s\n", filter)
259+
if len(strings.TrimSpace(options)) > 0 {
260+
fmt.Printf("Options: %s\n", options)
261+
}
262+
if strings.Contains(options, "background=true") {
263+
fmt.Printf("Showing last: %d\n", flowsToShow)
264+
fmt.Printf("Display: %s\n", strings.Join(display, ","))
265+
fmt.Printf("Enrichment: %s\n", strings.Join(enrichement, ","))
266+
} else {
267+
fmt.Printf("Showing last: %d Use Up / Down keyboard arrows to increase / decrease limit\n", flowsToShow)
268+
fmt.Printf("Display: %s Use Left / Right keyboard arrows to cycle views\n", strings.Join(display, ","))
269+
fmt.Printf("Enrichment: %s Use Page Up / Page Down keyboard keys to cycle enrichment scopes\n", strings.Join(enrichement, ","))
257270
}
258-
fmt.Printf("Showing last: %d Use Up / Down keyboard arrows to increase / decrease limit\n", flowsToShow)
259-
fmt.Printf("Display: %s Use Left / Right keyboard arrows to cycle views\n", strings.Join(display, ","))
260-
fmt.Printf("Enrichment: %s Use Page Up / Page Down keyboard keys to cycle enrichment scopes\n", strings.Join(enrichement, ","))
261271
}
262272

263273
if slices.Contains(display, rawDisplay) {
@@ -394,7 +404,6 @@ func updateTable() {
394404
fmt.Printf("Type anything to filter incoming flows in view\n")
395405
}
396406
}
397-
398407
}
399408
}
400409

cmd/packet_capture.go

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -158,17 +158,21 @@ func runPacketCaptureOnAddr(port int, filename string) {
158158
// terminate capture if max bytes reached
159159
totalBytes = totalBytes + int64(len(fp.GenericMap.Value))
160160
if totalBytes > maxBytes {
161-
log.Infof("Capture reached %s, exiting now...", sizestr.ToString(maxBytes))
162-
return
161+
if exit := onLimitReached(); exit {
162+
log.Infof("Capture reached %s, exiting now...", sizestr.ToString(maxBytes))
163+
return
164+
}
163165
}
164166
totalPackets = totalPackets + 1
165167

166168
// terminate capture if max time reached
167169
now := currentTime()
168170
duration := now.Sub(startupTime)
169171
if int(duration) > int(maxTime) {
170-
log.Infof("Capture reached %s, exiting now...", maxTime)
171-
return
172+
if exit := onLimitReached(); exit {
173+
log.Infof("Capture reached %s, exiting now...", maxTime)
174+
return
175+
}
172176
}
173177

174178
captureStarted = true

cmd/root.go

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ var (
2424
logLevel string
2525
ports []int
2626
nodes []string
27-
filter string
27+
options string
2828
maxTime time.Duration
2929
maxBytes int64
3030

@@ -57,6 +57,7 @@ var (
5757
outputBuffer *bytes.Buffer
5858
collectorStarted = false
5959
captureStarted = false
60+
captureEnded = false
6061
stopReceived = false
6162
useMocks = false
6263
keyboardError = ""
@@ -73,7 +74,7 @@ func init() {
7374
rootCmd.PersistentFlags().StringVarP(&logLevel, "loglevel", "l", "info", "Log level")
7475
rootCmd.PersistentFlags().IntSliceVarP(&ports, "ports", "", []int{9999}, "TCP ports to listen")
7576
rootCmd.PersistentFlags().StringSliceVarP(&nodes, "nodes", "", []string{""}, "Node names per port (optionnal)")
76-
rootCmd.PersistentFlags().StringVarP(&filter, "filter", "", "", "Filter(s)")
77+
rootCmd.PersistentFlags().StringVarP(&options, "options", "", "", "Options(s)")
7778
rootCmd.PersistentFlags().DurationVarP(&maxTime, "maxtime", "", 5*time.Minute, "Maximum capture time")
7879
rootCmd.PersistentFlags().Int64VarP(&maxBytes, "maxbytes", "", 50000000, "Maximum capture bytes")
7980
rootCmd.PersistentFlags().BoolVarP(&useMocks, "mock", "", false, "Use mock")
@@ -103,7 +104,8 @@ func onInit() {
103104
log.Fatalf("specified nodes names doesn't match ports length")
104105
}
105106

106-
log.Infof("Running network-observability-cli\nLog level: %s\nFilter(s): %s", logLevel, filter)
107+
printBanner()
108+
log.Infof("Log level: %s\nOption(s): %s", logLevel, options)
107109
showKernelVersion()
108110

109111
if useMocks {
@@ -112,6 +114,17 @@ func onInit() {
112114
}
113115
}
114116

117+
func printBanner() {
118+
fmt.Print(`
119+
------------------------------------------------------------------------
120+
_ _ _ _ ___ _ ___
121+
| \| |___| |_ ___| |__ ___ ___ _ ___ __ / __| | |_ _|
122+
| .' / -_) _/ _ \ '_ (_-</ -_) '_\ V / | (__| |__ | |
123+
|_|\_\___|\__\___/_.__/__/\___|_| \_/ \___|____|___|
124+
125+
------------------------------------------------------------------------`)
126+
}
127+
115128
func showKernelVersion() {
116129
output, err := exec.Command("uname", "-r").Output()
117130
if err != nil {
@@ -123,3 +136,34 @@ func showKernelVersion() {
123136
log.Infof("Kernel version: %s", strings.TrimSpace(string(output)))
124137
}
125138
}
139+
140+
func onLimitReached() bool {
141+
shouldExit := false
142+
if !captureEnded {
143+
if strings.Contains(options, "background=true") {
144+
captureEnded = true
145+
resetTerminal()
146+
out, err := exec.Command("/oc-netobserv", "stop").Output()
147+
if err != nil {
148+
log.Fatal(err)
149+
}
150+
fmt.Printf("%s", out)
151+
fmt.Print(`Thank you for using...`)
152+
printBanner()
153+
fmt.Print(`
154+
155+
- Download the generated output using 'oc netobserv copy' command
156+
157+
- Once finished, clean the collector pod using 'oc netobserv cleanup'
158+
159+
See you soon !
160+
161+
162+
`)
163+
} else {
164+
shouldExit = true
165+
}
166+
}
167+
168+
return shouldExit
169+
}

cmd/root_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ func TestDefaultArguments(t *testing.T) {
7373
assert.Equal(t, "info", logLevel)
7474
assert.Equal(t, []int{9999}, ports)
7575
assert.Equal(t, []string{""}, nodes)
76-
assert.Empty(t, filter)
76+
assert.Empty(t, options)
7777
}
7878

7979
func setup() {

commands/netobserv

Lines changed: 48 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,11 @@ if [ -z "${isE2E+x}" ]; then isE2E=false; fi
99
if [ -z "${captureStarted+x}" ]; then captureStarted=false; fi
1010
# prompt copy by default
1111
if [ -z "${copy+x}" ]; then copy="prompt"; fi
12+
# run foreground by default
13+
if [ -z "${runBackground+x}" ]; then runBackground="false"; fi
1214

13-
# interface filter such as 'br-ex' or pcap filter such as 'tcp,80'
14-
filter=""
15+
# options such as filters, background etc
16+
options=""
1517

1618
# CLI image to use
1719
img="quay.io/netobserv/network-observability-cli:main"
@@ -43,7 +45,7 @@ function flows() {
4345
exit 0 ;;
4446
*)
4547
shift # remove first argument
46-
filter="$*"
48+
options="$*"
4749
# run flows command
4850
command="flows" ;;
4951
esac
@@ -56,7 +58,7 @@ function packets() {
5658
exit 0 ;;
5759
*)
5860
shift # remove first argument
59-
filter="$*"
61+
options="$*"
6062
# run packets command
6163
command="packets" ;;
6264
esac
@@ -72,12 +74,15 @@ case "$1" in
7274
echo "Syntax: netobserv [flows|packets|cleanup] [options]"
7375
echo
7476
echo "commands:"
75-
echo " flows Capture flows information. You can specify an optional interface name as filter such as 'netobserv flows br-ex'."
77+
echo " flows Capture flows information in JSON format."
7678
echo " Options:"
7779
flows_usage
7880
echo " packets Capture packets information in pcap format."
7981
echo " Options:"
8082
packets_usage
83+
echo " follow Follow collector logs when running in background."
84+
echo " stop Stop collection by removing agent daemonset."
85+
echo " copy Copy generated files locally."
8186
echo " cleanup Remove netobserv components."
8287
echo " version Print software version."
8388
echo
@@ -90,6 +95,18 @@ case "$1" in
9095
flows $* ;;
9196
"packets")
9297
packets $* ;;
98+
"follow")
99+
# run follow command
100+
follow
101+
exit 0 ;;
102+
"stop")
103+
# run deleteDaemonset command
104+
deleteDaemonset
105+
exit 0 ;;
106+
"copy")
107+
# run copy output command
108+
copyOutput
109+
exit 0 ;;
93110
"cleanup")
94111
# run cleanup command
95112
cleanup
@@ -101,24 +118,44 @@ esac
101118

102119
trap cleanup EXIT
103120

104-
setup $command $filter
121+
setup $command $options
122+
123+
# convert options to string
124+
optionStr="${options//--/}"
125+
optionStr="${optionStr// /|}"
126+
127+
# prepare commands & args
128+
runCommand="sleep infinity"
129+
execCommand="/network-observability-cli get-$command ${optionStr:+"--options" "${optionStr}"} --loglevel $logLevel --maxtime $maxTime --maxbytes $maxBytes"
130+
if [[ "$runBackground" == "true" ]]; then
131+
runCommand="$execCommand & $runCommand"
132+
execCommand=""
133+
fi
105134

106135
echo "Running network-observability-cli get-$command... "
107136
${K8S_CLI_BIN} run \
108137
-n netobserv-cli \
109138
collector \
110139
--image=$img\
111140
--image-pull-policy='Always' \
141+
--overrides='{ "spec": { "serviceAccount": "netobserv-cli" } }' \
112142
--restart='Never' \
113-
--command -- sleep infinity
143+
--command -- $runCommand
114144

115145
${K8S_CLI_BIN} wait \
116146
-n netobserv-cli \
117147
--for=condition=Ready pod/collector || exit 1
118148

119149
captureStarted=true
120150

121-
${K8S_CLI_BIN} exec -i --tty \
122-
-n netobserv-cli \
123-
collector \
124-
-- /network-observability-cli get-$command ${filter:+"--filter" "${filter//--/}"} --loglevel $logLevel --maxtime $maxTime --maxbytes $maxBytes
151+
if [ -n "${execCommand}" ]; then
152+
${K8S_CLI_BIN} exec -i --tty \
153+
-n netobserv-cli \
154+
collector \
155+
-- $execCommand
156+
else
157+
echo "Background capture started. Use:"
158+
echo " - '${K8S_CLI_BIN} netobserv follow' to see the capture progress"
159+
echo " - '${K8S_CLI_BIN} netobserv copy' to copy the generated files locally"
160+
echo " - '${K8S_CLI_BIN} netobserv cleanup' to remove the netobserv components"
161+
fi

e2e/script_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ func TestHelpCommand(t *testing.T) {
3030
assert.Contains(t, str, "Find more information at: https://github.com/netobserv/network-observability-cli/")
3131
// ensure help to display proper options
3232
assert.Contains(t, str, "Syntax: netobserv [flows|packets|cleanup] [options]")
33-
assert.Contains(t, str, "flows Capture flows information. You can specify an optional interface name as filter such as 'netobserv flows br-ex'.")
33+
assert.Contains(t, str, "flows Capture flows information in JSON format.")
3434
assert.Contains(t, str, "packets Capture packets information in pcap format.")
3535
assert.Contains(t, str, "cleanup Remove netobserv components.")
3636
assert.Contains(t, str, "version Print software version.")

0 commit comments

Comments
 (0)