Skip to content

Commit

Permalink
Merge pull request #1241 from surik/add-events-command
Browse files Browse the repository at this point in the history
Add crictl events command
  • Loading branch information
k8s-ci-robot authored Aug 18, 2023
2 parents 6945f73 + 163da8b commit 28f1423
Show file tree
Hide file tree
Showing 7 changed files with 206 additions and 3 deletions.
102 changes: 102 additions & 0 deletions cmd/crictl/events.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/*
Copyright 2023 The Kubernetes Authors.
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 main

import (
"fmt"
"io"

"github.com/sirupsen/logrus"
"github.com/urfave/cli/v2"
internalapi "k8s.io/cri-api/pkg/apis"
pb "k8s.io/cri-api/pkg/apis/runtime/v1"
)

var eventsCommand = &cli.Command{
Name: "events",
Usage: "Stream the events of containers",
Aliases: []string{"event"},
UseShortOptionHandling: true,
Flags: []cli.Flag{
&cli.StringFlag{
Name: "output",
Aliases: []string{"o"},
Value: "json",
Usage: "Output format, One of: json|yaml|go-template",
},
&cli.StringFlag{
Name: "template",
Usage: "The template string is only used when output is go-template; The Template format is golang template",
},
},
Action: func(c *cli.Context) error {
if c.NArg() != 0 {
return cli.ShowSubcommandHelp(c)
}

switch format := c.String("output"); format {
case "json", "yaml":
if len(c.String("template")) > 0 {
return fmt.Errorf("template can't be used with %q format", format)
}
case "go-template":
if err := validateTemplate(c.String(("template"))); err != nil {
return fmt.Errorf("failed to parse go-template: %w", err)
}
default:
return fmt.Errorf("don't support %q format", format)
}

runtimeClient, err := getRuntimeService(c, 0)
if err != nil {
return err
}

if err = Events(c, runtimeClient); err != nil {
return fmt.Errorf("getting container events: %w", err)
}

return nil
},
}

func Events(cliContext *cli.Context, client internalapi.RuntimeService) error {
errCh := make(chan error, 1)

containerEventsCh := make(chan *pb.ContainerEventResponse)
go func() {
logrus.Debug("getting container events")
err := client.GetContainerEvents(containerEventsCh)
if err == io.EOF {
errCh <- nil
return
}
errCh <- err
}()

for {
select {
case err := <-errCh:
return err
case e := <-containerEventsCh:
err := outputEvent(e, cliContext.String("output"), cliContext.String("template"))
if err != nil {
fmt.Printf("failed to format container event with the error: %s\n", err)
}
}
}
}
1 change: 1 addition & 0 deletions cmd/crictl/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,7 @@ func main() {
completionCommand,
checkpointContainerCommand,
runtimeConfigCommand,
eventsCommand,
}

runtimeEndpointUsage := fmt.Sprintf("Endpoint of CRI container runtime "+
Expand Down
5 changes: 5 additions & 0 deletions cmd/crictl/templates.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,3 +68,8 @@ func tmplExecuteRawJSON(tmplStr string, rawJSON string) (string, error) {
}
return o.String(), nil
}

func validateTemplate(tmplStr string) error {
_, err := template.New("").Parse(tmplStr)
return err
}
28 changes: 28 additions & 0 deletions cmd/crictl/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,34 @@ func outputStatusInfo(status string, info map[string]string, format string, tmpl
return nil
}

func outputEvent(event proto.Message, format string, tmplStr string) error {
switch format {
case "yaml":
err := outputProtobufObjAsYAML(event)
if err != nil {
return err
}
case "json":
err := outputProtobufObjAsJSON(event)
if err != nil {
return err
}
case "go-template":
jsonEvent, err := protobufObjectToJSON(event)
if err != nil {
return err
}
output, err := tmplExecuteRawJSON(tmplStr, jsonEvent)
if err != nil {
return err
}
fmt.Println(output)
default:
fmt.Printf("Don't support %q format\n", format)
}
return nil
}

func parseLabelStringSlice(ss []string) (map[string]string, error) {
labels := make(map[string]string)
for _, s := range ss {
Expand Down
1 change: 1 addition & 0 deletions docs/crictl.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ COMMANDS:
- `statsp`: List pod(s) resource usage statistics
- `completion`: Output bash shell completion code
- `checkpoint`: Checkpoint one or more running containers
- `events, event`: Stream the events of containers
- `help, h`: Shows a list of commands or help for one command

`crictl` by default connects on Unix to:
Expand Down
60 changes: 60 additions & 0 deletions test/e2e/events_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
Copyright 2023 The Kubernetes Authors.
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 e2e

import (
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
. "github.com/onsi/gomega/gbytes"
. "github.com/onsi/gomega/gexec"
)

// The actual test suite
var _ = t.Describe("events options validation", func() {
It("should fail with not supported output format", func() {
t.CrictlExpectFailure("events --output=ini", "", "don't support .* format")
})

It("should fail with template set for non go-template format", func() {
t.CrictlExpectFailure("events --template={{.containerID}}", "", "template can't be used with .* format")
})

It("should fail with bad template set for go-template format", func() {
t.CrictlExpectFailure("events --output=go-template --template={{", "", "failed to parse go-template")
})
})

// The actual test suite
var _ = t.Describe("events", func() {
var (
endpoint, testDir string
crio *Session
)
BeforeEach(func() {
endpoint, testDir, crio = t.StartCrio()
})

AfterEach(func() {
t.StopCrio(testDir, crio)
})

It("should succeed", func() {
session := t.CrictlWithEndpointNoWait(endpoint, "events")
defer session.Terminate()
Expect(session.Out).ToNot(Say("unknown method GetContainerEvents")) // no errors
})
})
12 changes: 9 additions & 3 deletions test/framework/framework.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,11 @@ func (t *TestFramework) CrictlWithEndpoint(endpoint, args string) *Session {
return lcmd("crictl --runtime-endpoint=%s %s", endpoint, args).Wait(time.Minute)
}

// Run crictl on the specified endpoint and return the resulting session without wait
func (t *TestFramework) CrictlWithEndpointNoWait(endpoint, args string) *Session {
return lcmd("crictl --runtime-endpoint=%s %s", endpoint, args)
}

// Run crictl and expect exit, expectedOut, expectedErr
func (t *TestFramework) CrictlExpect(
endpoint, args string, exit int, expectedOut, expectedErr string,
Expand Down Expand Up @@ -136,9 +141,9 @@ func (t *TestFramework) CrictlExpectFailureWithEndpoint(
func SetupCrio() string {
const (
crioURL = "https://github.com/cri-o/cri-o"
crioVersion = "v1.23.1"
crioVersion = "v1.26.4"
conmonURL = "https://github.com/containers/conmon"
conmonVersion = "v2.0.32"
conmonVersion = "v2.1.7"
)
tmpDir := filepath.Join(os.TempDir(), "crio-tmp")

Expand Down Expand Up @@ -202,7 +207,8 @@ func (t *TestFramework) StartCrio() (string, string, *Session) {
" --cni-config-dir=%s"+
" --root=%s"+
" --runroot=%s"+
" --pinns-path=%s",
" --pinns-path=%s"+
" --enable-pod-events",
filepath.Join(tmpDir, "bin", "crio"),
filepath.Join(t.crioDir, "crio.conf"),
endpoint,
Expand Down

0 comments on commit 28f1423

Please sign in to comment.