Skip to content
This repository has been archived by the owner on Sep 13, 2024. It is now read-only.

Commit

Permalink
adding log package
Browse files Browse the repository at this point in the history
  • Loading branch information
PatrickLaabs committed Feb 19, 2024
1 parent 9411e5c commit a769988
Show file tree
Hide file tree
Showing 5 changed files with 234 additions and 5 deletions.
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ dist/
/git
bin
.vscode/settings.json
logs/
/tmp
lint_log.txt
.idea
Expand Down
97 changes: 97 additions & 0 deletions cmd/frigg/export/logs/logs.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/*
Copyright 2018 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 logs implements the `logs` command
package logs

import (
"fmt"

"github.com/spf13/cobra"

"github.com/PatrickLaabs/frigg/cmd"
"github.com/PatrickLaabs/frigg/pkg/cluster"
"github.com/PatrickLaabs/frigg/pkg/fs"
"github.com/PatrickLaabs/frigg/pkg/log"

"github.com/PatrickLaabs/frigg/internal/cli"

"github.com/PatrickLaabs/frigg/internal/runtime"
)

type flagpole struct {
Name string
}

// NewCommand returns a new cobra.Command for getting the cluster logs
func NewCommand(logger log.Logger, streams cmd.IOStreams) *cobra.Command {
flags := &flagpole{}
c := &cobra.Command{
Args: cobra.MaximumNArgs(1),
// TODO(bentheelder): more detailed usage
Use: "logs [output-dir]",
Short: "Exports logs to a tempdir or [output-dir] if specified",
Long: "Exports logs to a tempdir or [output-dir] if specified",
RunE: func(cmd *cobra.Command, args []string) error {
cli.OverrideDefaultName(cmd.Flags())
return runE(logger, streams, flags, args)
},
}
c.Flags().StringVarP(
&flags.Name,
"name",
"n",
cluster.DefaultName,
"the cluster context name",
)
return c
}

func runE(logger log.Logger, streams cmd.IOStreams, flags *flagpole, args []string) error {
provider := cluster.NewProvider(
cluster.ProviderWithLogger(logger),
runtime.GetDefault(logger),
)

// Check if the cluster has any running nodes
nodes, err := provider.ListNodes(flags.Name)
if err != nil {
return err
}
if len(nodes) == 0 {
return fmt.Errorf("unknown cluster %q", flags.Name)
}

// get the optional directory argument, or create a tempdir
var dir string
if len(args) == 0 {
t, err := fs.TempDir("", "")
if err != nil {
return err
}
dir = t
} else {
dir = args[0]
}

// NOTE: the path is the output of this command to be captured by calling tools
// whereas "Exporting logs..." is info / debug (stderr)
logger.V(0).Infof("Exporting logs for cluster %q to:", flags.Name)
fmt.Fprintln(streams.Out, dir)

// collect the logs
return provider.CollectLogs(flags.Name, dir)
}
18 changes: 18 additions & 0 deletions pkg/cluster/internal/logs/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/*
Copyright 2018 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 logs contains tooling for obtaining cluster logs
package logs
105 changes: 105 additions & 0 deletions pkg/cluster/internal/logs/logs.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/*
Copyright 2018 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 logs

import (
"archive/tar"
"fmt"
"io"
"os"
"path"
"path/filepath"

"github.com/alessio/shellescape"

"github.com/PatrickLaabs/frigg/pkg/cluster/nodes"
"github.com/PatrickLaabs/frigg/pkg/errors"
"github.com/PatrickLaabs/frigg/pkg/exec"
"github.com/PatrickLaabs/frigg/pkg/log"
)

// DumpDir dumps the dir nodeDir on the node to the dir hostDir on the host
func DumpDir(logger log.Logger, node nodes.Node, nodeDir, hostDir string) (err error) {
cmd := node.Command(
"sh", "-c",
// Tar will exit 1 if a file changed during the archival.
// We don't care about this, so we're invoking it in a shell
// And masking out 1 as a return value.
// Fatal errors will return exit code 2.
// http://man7.org/linux/man-pages/man1/tar.1.html#RETURN_VALUE
fmt.Sprintf(
`tar --hard-dereference -C %s -chf - . || (r=$?; [ $r -eq 1 ] || exit $r)`,
shellescape.Quote(path.Clean(nodeDir)+"/"),
),
)

return exec.RunWithStdoutReader(cmd, func(outReader io.Reader) error {
if err := untar(logger, outReader, hostDir); err != nil {
return errors.Wrapf(err, "Untarring %q: %v", nodeDir, err)
}
return nil
})
}

// untar reads the tar file from r and writes it into dir.
func untar(logger log.Logger, r io.Reader, dir string) (err error) {
tr := tar.NewReader(r)
for {
f, err := tr.Next()

switch {
case err == io.EOF:
// drain the reader, which may have trailing null bytes
// we don't want to leave the writer hanging
_, err := io.Copy(io.Discard, r)
return err
case err != nil:
return errors.Wrapf(err, "tar reading error: %v", err)
case f == nil:
continue
}

rel := filepath.FromSlash(f.Name)
abs := filepath.Join(dir, rel)

switch f.Typeflag {
case tar.TypeReg:
wf, err := os.OpenFile(abs, os.O_CREATE|os.O_RDWR, os.FileMode(f.Mode))
if err != nil {
return err
}
n, err := io.Copy(wf, tr)
if closeErr := wf.Close(); closeErr != nil && err == nil {
err = closeErr
}
if err != nil {
return errors.Errorf("error writing to %s: %v", abs, err)
}
if n != f.Size {
return errors.Errorf("only wrote %d bytes to %s; expected %d", n, abs, f.Size)
}
case tar.TypeDir:
if _, err := os.Stat(abs); err != nil {
if err := os.MkdirAll(abs, 0755); err != nil {
return err
}
}
default:
logger.Warnf("tar file entry %s contained unsupported file type %v", f.Name, f.Typeflag)
}
}
}
18 changes: 14 additions & 4 deletions pkg/cluster/internal/providers/docker/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ func (p *provider) DeleteNodes(n []nodes.Node) error {
args := make([]string, 0, len(n)+3) // allocate once
args = append(args,
"rm",
"-f", // force the container to be delete now
"-f", // force the container to be deleted now
"-v", // delete volumes
)
for _, node := range n {
Expand Down Expand Up @@ -223,7 +223,7 @@ func (p *provider) GetAPIServerInternalEndpoint(cluster string) (string, error)
if err != nil {
return "", errors.Wrap(err, "failed to get api server endpoint")
}
// NOTE: we're using the nodes's hostnames which are their names
// NOTE: we're using the node's hostnames which are their names
return net.JoinHostPort(n.String(), fmt.Sprintf("%d", common.APIServerInternalPort)), nil
}

Expand All @@ -242,7 +242,12 @@ func (p *provider) CollectLogs(dir string, nodes []nodes.Node) error {
if err != nil {
return err
}
defer f.Close()
defer func(f *os.File) {
err := f.Close()
if err != nil {

}
}(f)
return cmd.SetStdout(f).SetStderr(f).Run()
}
}
Expand Down Expand Up @@ -273,7 +278,12 @@ func (p *provider) CollectLogs(dir string, nodes []nodes.Node) error {
if err != nil {
return err
}
defer f.Close()
defer func(f *os.File) {
err := f.Close()
if err != nil {

}
}(f)
return node.SerialLogs(f)
},
)
Expand Down

0 comments on commit a769988

Please sign in to comment.