mirror of
https://github.com/arangodb/kube-arangodb.git
synced 2024-12-14 11:57:37 +00:00
[Feature] [Debug Package] Initial commit (#1117)
This commit is contained in:
parent
efdcd1bd2e
commit
76289415c6
8 changed files with 622 additions and 1 deletions
|
@ -4,8 +4,8 @@
|
|||
- (Feature) Add new field to DeploymentReplicationStatus with details on DC2DC sync status
|
||||
- (Feature) Early connections support
|
||||
- (Bugfix) Fix and document action timeouts
|
||||
|
||||
- (Feature) Propagate sidecars' ports to a member's service
|
||||
- (Debug Package) Initial commit
|
||||
|
||||
## [1.2.16](https://github.com/arangodb/kube-arangodb/tree/1.2.16) (2022-09-14)
|
||||
- (Feature) Add ArangoDeployment ServerGroupStatus
|
||||
|
|
65
cmd/debug.go
Normal file
65
cmd/debug.go
Normal file
|
@ -0,0 +1,65 @@
|
|||
//
|
||||
// DISCLAIMER
|
||||
//
|
||||
// Copyright 2016-2022 ArangoDB GmbH, Cologne, Germany
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
// Copyright holder is ArangoDB GmbH, Cologne, Germany
|
||||
//
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"compress/gzip"
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/arangodb/kube-arangodb/pkg/debug_package"
|
||||
)
|
||||
|
||||
func init() {
|
||||
cmdMain.AddCommand(debugPackage)
|
||||
|
||||
debug_package.InitCommand(debugPackage)
|
||||
}
|
||||
|
||||
var debugPackage = &cobra.Command{
|
||||
Use: "debugPackage",
|
||||
Short: "[WiP] Generate debug package for debugging",
|
||||
RunE: debugPackageFunc,
|
||||
}
|
||||
|
||||
func debugPackageFunc(cmd *cobra.Command, _ []string) error {
|
||||
f, err := os.OpenFile("./out.tar.gz", os.O_TRUNC|os.O_WRONLY|os.O_CREATE, 0644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
gw := gzip.NewWriter(f)
|
||||
|
||||
if err := debug_package.GenerateD(cmd, gw); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := gw.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := f.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
155
pkg/debug_package/generator.go
Normal file
155
pkg/debug_package/generator.go
Normal file
|
@ -0,0 +1,155 @@
|
|||
//
|
||||
// DISCLAIMER
|
||||
//
|
||||
// Copyright 2016-2022 ArangoDB GmbH, Cologne, Germany
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
// Copyright holder is ArangoDB GmbH, Cologne, Germany
|
||||
//
|
||||
|
||||
package debug_package
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"bytes"
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/arangodb/kube-arangodb/pkg/debug_package/generators/kubernetes"
|
||||
"github.com/arangodb/kube-arangodb/pkg/debug_package/shared"
|
||||
"github.com/arangodb/kube-arangodb/pkg/util/errors"
|
||||
)
|
||||
|
||||
var rootFactories = []shared.Factory{
|
||||
kubernetes.Events(),
|
||||
kubernetes.Pods(),
|
||||
kubernetes.Secrets(),
|
||||
}
|
||||
|
||||
func InitCommand(cmd *cobra.Command) {
|
||||
for _, f := range rootFactories {
|
||||
f.Init(cmd)
|
||||
}
|
||||
}
|
||||
|
||||
func GenerateD(cmd *cobra.Command, out io.Writer) error {
|
||||
return Generate(cmd, out, rootFactories...)
|
||||
}
|
||||
|
||||
func Generate(cmd *cobra.Command, out io.Writer, factories ...shared.Factory) error {
|
||||
tw := tar.NewWriter(out)
|
||||
|
||||
data := bytes.NewBuffer(nil)
|
||||
|
||||
log := NewLogger(data)
|
||||
|
||||
files := make(chan shared.File)
|
||||
|
||||
fileErrors := map[string]error{}
|
||||
factoryErrors := map[string]error{}
|
||||
|
||||
done := make(chan struct{})
|
||||
|
||||
n := time.Now()
|
||||
|
||||
go func() {
|
||||
defer close(done)
|
||||
for file := range files {
|
||||
log.Info().Msgf("Fetching file %s", file.Path())
|
||||
data, err := file.Write()
|
||||
if err != nil {
|
||||
fileErrors[file.Path()] = err
|
||||
continue
|
||||
}
|
||||
|
||||
if err := tw.WriteHeader(&tar.Header{
|
||||
Name: file.Path(),
|
||||
ModTime: n,
|
||||
AccessTime: n,
|
||||
ChangeTime: n,
|
||||
Mode: 0644,
|
||||
Uid: 1000,
|
||||
Gid: 1000,
|
||||
Size: int64(len(data)),
|
||||
}); err != nil {
|
||||
fileErrors[file.Path()] = err
|
||||
continue
|
||||
}
|
||||
|
||||
if _, err := tw.Write(data); err != nil {
|
||||
fileErrors[file.Path()] = err
|
||||
continue
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
for _, f := range factories {
|
||||
log.Info().Msgf("Fetching factory %s", f.Name())
|
||||
if err := f.Generate(cmd, log, files); err != nil {
|
||||
factoryErrors[f.Name()] = err
|
||||
}
|
||||
}
|
||||
|
||||
close(files)
|
||||
|
||||
<-done
|
||||
|
||||
if len(fileErrors) > 0 {
|
||||
log.Error().Msgf("%d errors while fetching files:", len(fileErrors))
|
||||
|
||||
parsedErrors := map[string]string{}
|
||||
|
||||
for f, n := range fileErrors {
|
||||
parsedErrors[f] = n.Error()
|
||||
log.Error().Msgf("\t%s: %s", f, n.Error())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if len(factoryErrors) > 0 {
|
||||
log.Error().Msgf("%d errors while fetching factories:", len(factoryErrors))
|
||||
for f, n := range factoryErrors {
|
||||
log.Error().Msgf("\t%s: %s", f, n.Error())
|
||||
}
|
||||
}
|
||||
|
||||
if err := tw.WriteHeader(&tar.Header{
|
||||
Name: "logs",
|
||||
ModTime: n,
|
||||
AccessTime: n,
|
||||
ChangeTime: n,
|
||||
Mode: 0644,
|
||||
Uid: 1000,
|
||||
Gid: 1000,
|
||||
Size: int64(len(data.Bytes())),
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := io.Copy(tw, data); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := tw.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(fileErrors) > 0 || len(factoryErrors) > 0 {
|
||||
return errors.Newf("Error while receiving data")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
84
pkg/debug_package/generators/kubernetes/events.go
Normal file
84
pkg/debug_package/generators/kubernetes/events.go
Normal file
|
@ -0,0 +1,84 @@
|
|||
//
|
||||
// DISCLAIMER
|
||||
//
|
||||
// Copyright 2016-2022 ArangoDB GmbH, Cologne, Germany
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
// Copyright holder is ArangoDB GmbH, Cologne, Germany
|
||||
//
|
||||
|
||||
package kubernetes
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/spf13/cobra"
|
||||
eventsv1 "k8s.io/api/events/v1"
|
||||
meta "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
|
||||
"github.com/arangodb/kube-arangodb/pkg/debug_package/shared"
|
||||
"github.com/arangodb/kube-arangodb/pkg/util/errors"
|
||||
"github.com/arangodb/kube-arangodb/pkg/util/kclient"
|
||||
)
|
||||
|
||||
func Events() shared.Factory {
|
||||
return shared.NewFactory("kubernetes-events", func(cmd *cobra.Command) {
|
||||
f := cmd.Flags()
|
||||
if f.Lookup("namespace") == nil {
|
||||
f.String("namespace", "default", "Kubernetes namespace")
|
||||
}
|
||||
}, func(cmd *cobra.Command, logger zerolog.Logger, files chan<- shared.File) error {
|
||||
k, ok := kclient.GetDefaultFactory().Client()
|
||||
if !ok {
|
||||
return errors.Newf("Client is not initialised")
|
||||
}
|
||||
|
||||
ns, _ := cmd.Flags().GetString("namespace")
|
||||
|
||||
events := map[types.UID]*eventsv1.Event{}
|
||||
next := ""
|
||||
for {
|
||||
r, err := k.Kubernetes().EventsV1().Events(ns).List(context.Background(), meta.ListOptions{
|
||||
Continue: next,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, e := range r.Items {
|
||||
events[e.UID] = e.DeepCopy()
|
||||
}
|
||||
|
||||
next = r.Continue
|
||||
if next == "" {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
files <- shared.NewJSONFile("kubernetes/events.json", func() (interface{}, error) {
|
||||
q := make([]*eventsv1.Event, 0, len(events))
|
||||
|
||||
for _, e := range events {
|
||||
q = append(q, e)
|
||||
}
|
||||
|
||||
return q, nil
|
||||
})
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
84
pkg/debug_package/generators/kubernetes/pods.go
Normal file
84
pkg/debug_package/generators/kubernetes/pods.go
Normal file
|
@ -0,0 +1,84 @@
|
|||
//
|
||||
// DISCLAIMER
|
||||
//
|
||||
// Copyright 2016-2022 ArangoDB GmbH, Cologne, Germany
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
// Copyright holder is ArangoDB GmbH, Cologne, Germany
|
||||
//
|
||||
|
||||
package kubernetes
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/spf13/cobra"
|
||||
core "k8s.io/api/core/v1"
|
||||
meta "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
|
||||
"github.com/arangodb/kube-arangodb/pkg/debug_package/shared"
|
||||
"github.com/arangodb/kube-arangodb/pkg/util/errors"
|
||||
"github.com/arangodb/kube-arangodb/pkg/util/kclient"
|
||||
)
|
||||
|
||||
func Pods() shared.Factory {
|
||||
return shared.NewFactory("kubernetes-pods", func(cmd *cobra.Command) {
|
||||
f := cmd.Flags()
|
||||
if f.Lookup("namespace") == nil {
|
||||
f.String("namespace", "default", "Kubernetes namespace")
|
||||
}
|
||||
}, func(cmd *cobra.Command, logger zerolog.Logger, files chan<- shared.File) error {
|
||||
k, ok := kclient.GetDefaultFactory().Client()
|
||||
if !ok {
|
||||
return errors.Newf("Client is not initialised")
|
||||
}
|
||||
|
||||
ns, _ := cmd.Flags().GetString("namespace")
|
||||
|
||||
pods := map[types.UID]*core.Pod{}
|
||||
next := ""
|
||||
for {
|
||||
r, err := k.Kubernetes().CoreV1().Pods(ns).List(context.Background(), meta.ListOptions{
|
||||
Continue: next,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, e := range r.Items {
|
||||
pods[e.UID] = e.DeepCopy()
|
||||
}
|
||||
|
||||
next = r.Continue
|
||||
if next == "" {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
files <- shared.NewJSONFile("kubernetes/pods.json", func() (interface{}, error) {
|
||||
q := make([]*core.Pod, 0, len(pods))
|
||||
|
||||
for _, e := range pods {
|
||||
q = append(q, e)
|
||||
}
|
||||
|
||||
return q, nil
|
||||
})
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
100
pkg/debug_package/generators/kubernetes/secrets.go
Normal file
100
pkg/debug_package/generators/kubernetes/secrets.go
Normal file
|
@ -0,0 +1,100 @@
|
|||
//
|
||||
// DISCLAIMER
|
||||
//
|
||||
// Copyright 2016-2022 ArangoDB GmbH, Cologne, Germany
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
// Copyright holder is ArangoDB GmbH, Cologne, Germany
|
||||
//
|
||||
|
||||
package kubernetes
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/spf13/cobra"
|
||||
core "k8s.io/api/core/v1"
|
||||
meta "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
|
||||
"github.com/arangodb/kube-arangodb/pkg/debug_package/shared"
|
||||
"github.com/arangodb/kube-arangodb/pkg/util/errors"
|
||||
"github.com/arangodb/kube-arangodb/pkg/util/kclient"
|
||||
)
|
||||
|
||||
func Secrets() shared.Factory {
|
||||
return shared.NewFactory("kubernetes-secrets", func(cmd *cobra.Command) {
|
||||
f := cmd.Flags()
|
||||
if f.Lookup("namespace") == nil {
|
||||
f.String("namespace", "default", "Kubernetes namespace")
|
||||
}
|
||||
if f.Lookup("hide-sensitive-data") == nil {
|
||||
f.Bool("hide-sensitive-data", true, "Hide sensitive data")
|
||||
}
|
||||
}, func(cmd *cobra.Command, logger zerolog.Logger, files chan<- shared.File) error {
|
||||
k, ok := kclient.GetDefaultFactory().Client()
|
||||
if !ok {
|
||||
return errors.Newf("Client is not initialised")
|
||||
}
|
||||
|
||||
ns, _ := cmd.Flags().GetString("namespace")
|
||||
|
||||
secrets := map[types.UID]*core.Secret{}
|
||||
next := ""
|
||||
for {
|
||||
r, err := k.Kubernetes().CoreV1().Secrets(ns).List(context.Background(), meta.ListOptions{
|
||||
Continue: next,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sensitive, _ := cmd.Flags().GetBool("hide-sensitive-data")
|
||||
|
||||
for _, e := range r.Items {
|
||||
hashed := make(map[string][]byte, len(e.Data))
|
||||
for k, v := range e.Data {
|
||||
if sensitive {
|
||||
hashed[k] = []byte(fmt.Sprintf("%02x", sha256.Sum256(v)))
|
||||
} else {
|
||||
hashed[k] = v
|
||||
}
|
||||
}
|
||||
secrets[e.UID] = e.DeepCopy()
|
||||
secrets[e.UID].Data = hashed
|
||||
}
|
||||
|
||||
next = r.Continue
|
||||
if next == "" {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
files <- shared.NewJSONFile("kubernetes/secrets.json", func() (interface{}, error) {
|
||||
q := make([]*core.Secret, 0, len(secrets))
|
||||
|
||||
for _, e := range secrets {
|
||||
q = append(q, e)
|
||||
}
|
||||
|
||||
return q, nil
|
||||
})
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
33
pkg/debug_package/out.go
Normal file
33
pkg/debug_package/out.go
Normal file
|
@ -0,0 +1,33 @@
|
|||
//
|
||||
// DISCLAIMER
|
||||
//
|
||||
// Copyright 2016-2022 ArangoDB GmbH, Cologne, Germany
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
// Copyright holder is ArangoDB GmbH, Cologne, Germany
|
||||
//
|
||||
|
||||
package debug_package
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
)
|
||||
|
||||
func NewLogger(out io.Writer) zerolog.Logger {
|
||||
return zerolog.New(zerolog.ConsoleWriter{Out: io.MultiWriter(out, os.Stdout), TimeFormat: time.RFC3339}).With().Timestamp().Logger()
|
||||
}
|
100
pkg/debug_package/shared/file.go
Normal file
100
pkg/debug_package/shared/file.go
Normal file
|
@ -0,0 +1,100 @@
|
|||
//
|
||||
// DISCLAIMER
|
||||
//
|
||||
// Copyright 2016-2022 ArangoDB GmbH, Cologne, Germany
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
// Copyright holder is ArangoDB GmbH, Cologne, Germany
|
||||
//
|
||||
|
||||
package shared
|
||||
|
||||
import (
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/spf13/cobra"
|
||||
"k8s.io/apimachinery/pkg/util/json"
|
||||
)
|
||||
|
||||
type GenFunc func(cmd *cobra.Command, logger zerolog.Logger, files chan<- File) error
|
||||
|
||||
type File interface {
|
||||
Path() string
|
||||
Write() ([]byte, error)
|
||||
}
|
||||
|
||||
func NewJSONFile(path string, write func() (interface{}, error)) File {
|
||||
return NewFile(path, func() ([]byte, error) {
|
||||
obj, err := write()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return json.Marshal(obj)
|
||||
})
|
||||
}
|
||||
|
||||
func NewFile(path string, write func() ([]byte, error)) File {
|
||||
return file{
|
||||
name: path,
|
||||
write: write,
|
||||
}
|
||||
}
|
||||
|
||||
type file struct {
|
||||
name string
|
||||
write func() ([]byte, error)
|
||||
}
|
||||
|
||||
func (f file) Path() string {
|
||||
return f.name
|
||||
}
|
||||
|
||||
func (f file) Write() ([]byte, error) {
|
||||
return f.write()
|
||||
}
|
||||
|
||||
type Factory interface {
|
||||
Name() string
|
||||
Generate(cmd *cobra.Command, logger zerolog.Logger, files chan<- File) error
|
||||
|
||||
Init(command *cobra.Command)
|
||||
}
|
||||
|
||||
func NewFactory(name string, cmd func(cmd *cobra.Command), gen GenFunc) Factory {
|
||||
return factory{
|
||||
name: name,
|
||||
generate: gen,
|
||||
cmd: cmd,
|
||||
}
|
||||
}
|
||||
|
||||
type factory struct {
|
||||
name string
|
||||
generate GenFunc
|
||||
cmd func(cmd *cobra.Command)
|
||||
}
|
||||
|
||||
func (f factory) Init(command *cobra.Command) {
|
||||
if c := f.cmd; c != nil {
|
||||
c(command)
|
||||
}
|
||||
}
|
||||
|
||||
func (f factory) Name() string {
|
||||
return f.name
|
||||
}
|
||||
|
||||
func (f factory) Generate(cmd *cobra.Command, logger zerolog.Logger, files chan<- File) error {
|
||||
return f.generate(cmd, logger, files)
|
||||
}
|
Loading…
Reference in a new issue