From 92d82ceaee23464ea5e026121dfc366a621bdacb Mon Sep 17 00:00:00 2001 From: Christopher Desiniotis Date: Mon, 27 Mar 2023 22:22:36 -0700 Subject: [PATCH 1/5] Add 'target-driver-root' option to 'nvidia-ctk cdi generate' to transform root paths in generated spec Signed-off-by: Christopher Desiniotis --- cmd/nvidia-ctk/cdi/generate/generate.go | 27 ++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/cmd/nvidia-ctk/cdi/generate/generate.go b/cmd/nvidia-ctk/cdi/generate/generate.go index b67bcb725..6f896952d 100644 --- a/cmd/nvidia-ctk/cdi/generate/generate.go +++ b/cmd/nvidia-ctk/cdi/generate/generate.go @@ -26,6 +26,7 @@ import ( "github.com/NVIDIA/nvidia-container-toolkit/internal/edits" "github.com/NVIDIA/nvidia-container-toolkit/pkg/nvcdi" "github.com/NVIDIA/nvidia-container-toolkit/pkg/nvcdi/spec" + "github.com/NVIDIA/nvidia-container-toolkit/pkg/nvcdi/transform" "github.com/container-orchestrated-devices/container-device-interface/pkg/cdi" specs "github.com/container-orchestrated-devices/container-device-interface/specs-go" "github.com/sirupsen/logrus" @@ -45,6 +46,7 @@ type config struct { format string deviceNameStrategy string driverRoot string + targetDriverRoot string nvidiaCTKPath string mode string } @@ -108,6 +110,12 @@ func (m command) build() *cli.Command { Usage: "Specify the path to use for the nvidia-ctk in the generated CDI specification. If this is left empty, the path will be searched.", Destination: &cfg.nvidiaCTKPath, }, + &cli.StringFlag{ + Name: "target-driver-root", + Usage: "Specify the NVIDIA GPU driver root to use in the generated CDI specification. Only used if not empty and differs from 'driver-root' used during discovery.", + Value: "", + Destination: &cfg.targetDriverRoot, + }, } return &c @@ -140,6 +148,10 @@ func (m command) validateFlags(c *cli.Context, cfg *config) error { cfg.nvidiaCTKPath = discover.FindNvidiaCTK(m.logger, cfg.nvidiaCTKPath) + if cfg.targetDriverRoot == "" { + cfg.targetDriverRoot = cfg.driverRoot + } + if outputFileFormat := formatFromFilename(cfg.output); outputFileFormat != "" { m.logger.Debugf("Inferred output format as %q from output file name", outputFileFormat) if !c.IsSet("format") { @@ -223,13 +235,26 @@ func (m command) generateSpec(cfg *config) (spec.Interface, error) { return nil, fmt.Errorf("failed to create edits common for entities: %v", err) } - return spec.New( + spec, err := spec.New( spec.WithVendor("nvidia.com"), spec.WithClass("gpu"), spec.WithDeviceSpecs(deviceSpecs), spec.WithEdits(*commonEdits.ContainerEdits), spec.WithFormat(cfg.format), ) + if err != nil { + return nil, err + } + + err = transform.NewRootTransformer( + cfg.driverRoot, + cfg.targetDriverRoot, + ).Transform(spec.Raw()) + if err != nil { + return nil, fmt.Errorf("failed to transform driver root in CDI spec: %v", err) + } + + return spec, err } // MergeDeviceSpecs creates a device with the specified name which combines the edits from the previous devices. From 7f5c9abc1e0e9613cf21d0c2730e0c2a4368bba0 Mon Sep 17 00:00:00 2001 From: Christopher Desiniotis Date: Mon, 27 Mar 2023 23:12:00 -0700 Subject: [PATCH 2/5] Add ability to configure CDI kind with 'nvidia-ctk cdi generate' Signed-off-by: Christopher Desiniotis --- cmd/nvidia-ctk/cdi/generate/generate.go | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/cmd/nvidia-ctk/cdi/generate/generate.go b/cmd/nvidia-ctk/cdi/generate/generate.go index 6f896952d..37bb562e8 100644 --- a/cmd/nvidia-ctk/cdi/generate/generate.go +++ b/cmd/nvidia-ctk/cdi/generate/generate.go @@ -49,6 +49,9 @@ type config struct { targetDriverRoot string nvidiaCTKPath string mode string + kind string + vendor string + class string } // NewCommand constructs a generate-cdi command with the specified logger @@ -116,6 +119,13 @@ func (m command) build() *cli.Command { Value: "", Destination: &cfg.targetDriverRoot, }, + &cli.StringFlag{ + Name: "kind", + Aliases: []string{"cdi-kind"}, + Usage: "the vendor string to use for the generated CDI specification.", + Value: "nvidia.com/gpu", + Destination: &cfg.kind, + }, } return &c @@ -161,6 +171,16 @@ func (m command) validateFlags(c *cli.Context, cfg *config) error { } } + vendor, class := cdi.ParseQualifier(cfg.kind) + if err := cdi.ValidateVendorName(vendor); err != nil { + return fmt.Errorf("invalid CDI vendor name: %v", err) + } + if err := cdi.ValidateClassName(class); err != nil { + return fmt.Errorf("invalid CDI class name: %v", err) + } + cfg.vendor = vendor + cfg.class = class + return nil } @@ -236,8 +256,8 @@ func (m command) generateSpec(cfg *config) (spec.Interface, error) { } spec, err := spec.New( - spec.WithVendor("nvidia.com"), - spec.WithClass("gpu"), + spec.WithVendor(cfg.vendor), + spec.WithClass(cfg.class), spec.WithDeviceSpecs(deviceSpecs), spec.WithEdits(*commonEdits.ContainerEdits), spec.WithFormat(cfg.format), From 6a83e2ebe55265a9f3a9da5fd13b7f3a5afc5810 Mon Sep 17 00:00:00 2001 From: Evan Lezar Date: Tue, 28 Mar 2023 12:18:32 +0200 Subject: [PATCH 3/5] Add nvidia-ctk cdi transform root command Signed-off-by: Evan Lezar --- cmd/nvidia-ctk/cdi/cdi.go | 2 + cmd/nvidia-ctk/cdi/transform/root/root.go | 104 ++++++++++++++++++ cmd/nvidia-ctk/cdi/transform/transform.go | 128 ++++++++++++++++++++++ pkg/nvcdi/spec/builder.go | 15 ++- 4 files changed, 248 insertions(+), 1 deletion(-) create mode 100644 cmd/nvidia-ctk/cdi/transform/root/root.go create mode 100644 cmd/nvidia-ctk/cdi/transform/transform.go diff --git a/cmd/nvidia-ctk/cdi/cdi.go b/cmd/nvidia-ctk/cdi/cdi.go index 84b42717a..e6c75a2d3 100644 --- a/cmd/nvidia-ctk/cdi/cdi.go +++ b/cmd/nvidia-ctk/cdi/cdi.go @@ -18,6 +18,7 @@ package cdi import ( "github.com/NVIDIA/nvidia-container-toolkit/cmd/nvidia-ctk/cdi/generate" + "github.com/NVIDIA/nvidia-container-toolkit/cmd/nvidia-ctk/cdi/transform" "github.com/sirupsen/logrus" "github.com/urfave/cli/v2" ) @@ -44,6 +45,7 @@ func (m command) build() *cli.Command { hook.Subcommands = []*cli.Command{ generate.NewCommand(m.logger), + transform.NewCommand(m.logger), } return &hook diff --git a/cmd/nvidia-ctk/cdi/transform/root/root.go b/cmd/nvidia-ctk/cdi/transform/root/root.go new file mode 100644 index 000000000..6014cea8f --- /dev/null +++ b/cmd/nvidia-ctk/cdi/transform/root/root.go @@ -0,0 +1,104 @@ +/** +# Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved. +# +# 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 root + +import ( + "fmt" + + "github.com/NVIDIA/nvidia-container-toolkit/pkg/nvcdi/spec" + "github.com/NVIDIA/nvidia-container-toolkit/pkg/nvcdi/transform" + "github.com/sirupsen/logrus" + "github.com/urfave/cli/v2" +) + +type loadSaver interface { + Load() (spec.Interface, error) + Save(spec.Interface) error +} + +type command struct { + logger *logrus.Logger + + handler loadSaver +} + +type config struct { + from string + to string +} + +// NewCommand constructs a generate-cdi command with the specified logger +func NewCommand(logger *logrus.Logger, specHandler loadSaver) *cli.Command { + c := command{ + logger: logger, + handler: specHandler, + } + return c.build() +} + +// build creates the CLI command +func (m command) build() *cli.Command { + cfg := config{} + + c := cli.Command{ + Name: "root", + Usage: "Apply a root transform to a CDI specification", + Before: func(c *cli.Context) error { + return m.validateFlags(c, &cfg) + }, + Action: func(c *cli.Context) error { + return m.run(c, &cfg) + }, + } + + c.Flags = []cli.Flag{ + &cli.StringFlag{ + Name: "from", + Usage: "specify the root to be transformed", + Destination: &cfg.from, + }, + &cli.StringFlag{ + Name: "to", + Usage: "specify the replacement root. If this is the same as the from root, the transform is a no-op.", + Value: "", + Destination: &cfg.to, + }, + } + + return &c +} + +func (m command) validateFlags(c *cli.Context, cfg *config) error { + return nil +} + +func (m command) run(c *cli.Context, cfg *config) error { + spec, err := m.handler.Load() + if err != nil { + return fmt.Errorf("failed to load CDI specification: %w", err) + } + + err = transform.NewRootTransformer( + cfg.from, + cfg.to, + ).Transform(spec.Raw()) + if err != nil { + return fmt.Errorf("failed to transform CDI specification: %w", err) + } + + return m.handler.Save(spec) +} diff --git a/cmd/nvidia-ctk/cdi/transform/transform.go b/cmd/nvidia-ctk/cdi/transform/transform.go new file mode 100644 index 000000000..22ad2e136 --- /dev/null +++ b/cmd/nvidia-ctk/cdi/transform/transform.go @@ -0,0 +1,128 @@ +/** +# Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved. +# +# 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 transform + +import ( + "fmt" + "io" + "os" + + "github.com/NVIDIA/nvidia-container-toolkit/cmd/nvidia-ctk/cdi/transform/root" + "github.com/NVIDIA/nvidia-container-toolkit/pkg/nvcdi/spec" + "github.com/container-orchestrated-devices/container-device-interface/pkg/cdi" + "github.com/sirupsen/logrus" + "github.com/urfave/cli/v2" +) + +type command struct { + logger *logrus.Logger +} + +type options struct { + input string + output string +} + +// NewCommand constructs a command with the specified logger +func NewCommand(logger *logrus.Logger) *cli.Command { + c := command{ + logger: logger, + } + return c.build() +} + +// build creates the CLI command +func (m command) build() *cli.Command { + opts := options{} + + c := cli.Command{ + Name: "transform", + Usage: "Apply a transform to a CDI specification", + Before: func(c *cli.Context) error { + return m.validateFlags(c, &opts) + }, + Action: func(c *cli.Context) error { + return m.run(c, &opts) + }, + } + + c.Flags = []cli.Flag{ + &cli.StringFlag{ + Name: "input", + Usage: "Specify the file to read the CDI specification from. If this is '-' the specification is read from STDIN", + Value: "-", + Destination: &opts.input, + }, + &cli.StringFlag{ + Name: "output", + Usage: "Specify the file to output the generated CDI specification to. If this is '' the specification is output to STDOUT", + Destination: &opts.output, + }, + } + + c.Subcommands = []*cli.Command{ + root.NewCommand(m.logger, &opts), + } + + return &c +} + +func (m command) validateFlags(c *cli.Context, opts *options) error { + return nil +} + +func (m command) run(c *cli.Context, cfg *options) error { + return nil +} + +// Load lodas the input CDI specification +func (o options) Load() (spec.Interface, error) { + contents, err := o.getContents() + if err != nil { + return nil, fmt.Errorf("failed to read spec contents: %v", err) + } + + raw, err := cdi.ParseSpec(contents) + if err != nil { + return nil, fmt.Errorf("failed to parse CDI spec: %v", err) + } + + return spec.New( + spec.WithRawSpec(raw), + ) +} + +func (o options) getContents() ([]byte, error) { + if o.input == "-" { + return io.ReadAll(os.Stdin) + } + + return os.ReadFile(o.input) +} + +// Save saves the CDI specification to the output file +func (o options) Save(s spec.Interface) error { + if o.output == "" { + _, err := s.WriteTo(os.Stdout) + if err != nil { + return fmt.Errorf("failed to write CDI spec to STDOUT: %v", err) + } + return nil + } + + return s.Save(o.output) +} diff --git a/pkg/nvcdi/spec/builder.go b/pkg/nvcdi/spec/builder.go index 78df7c175..6379ad0f2 100644 --- a/pkg/nvcdi/spec/builder.go +++ b/pkg/nvcdi/spec/builder.go @@ -41,6 +41,13 @@ func newBuilder(opts ...Option) *builder { for _, opt := range opts { opt(s) } + if s.raw != nil { + s.noSimplify = true + vendor, class := cdi.ParseQualifier(s.raw.Kind) + s.vendor = vendor + s.class = class + } + if s.version == "" { s.version = DetectMinimumVersion } @@ -60,7 +67,6 @@ func newBuilder(opts ...Option) *builder { // Build builds a CDI spec form the spec builder. func (o *builder) Build() (*spec, error) { raw := o.raw - if raw == nil { raw = &specs.Spec{ Version: o.version, @@ -144,3 +150,10 @@ func WithNoSimplify(noSimplify bool) Option { o.noSimplify = noSimplify } } + +// WithRawSpec sets the raw spec for the spec builder +func WithRawSpec(raw *specs.Spec) Option { + return func(o *builder) { + o.raw = raw + } +} From f27c33b45f41b86bfc04013bada8d37d8255ab18 Mon Sep 17 00:00:00 2001 From: Evan Lezar Date: Tue, 28 Mar 2023 12:31:09 +0200 Subject: [PATCH 4/5] Remove target-driver-root from generate Signed-off-by: Evan Lezar --- cmd/nvidia-ctk/cdi/generate/generate.go | 49 +++++++------------------ 1 file changed, 13 insertions(+), 36 deletions(-) diff --git a/cmd/nvidia-ctk/cdi/generate/generate.go b/cmd/nvidia-ctk/cdi/generate/generate.go index 37bb562e8..6ab27b007 100644 --- a/cmd/nvidia-ctk/cdi/generate/generate.go +++ b/cmd/nvidia-ctk/cdi/generate/generate.go @@ -26,7 +26,6 @@ import ( "github.com/NVIDIA/nvidia-container-toolkit/internal/edits" "github.com/NVIDIA/nvidia-container-toolkit/pkg/nvcdi" "github.com/NVIDIA/nvidia-container-toolkit/pkg/nvcdi/spec" - "github.com/NVIDIA/nvidia-container-toolkit/pkg/nvcdi/transform" "github.com/container-orchestrated-devices/container-device-interface/pkg/cdi" specs "github.com/container-orchestrated-devices/container-device-interface/specs-go" "github.com/sirupsen/logrus" @@ -46,10 +45,8 @@ type config struct { format string deviceNameStrategy string driverRoot string - targetDriverRoot string nvidiaCTKPath string mode string - kind string vendor string class string } @@ -114,17 +111,18 @@ func (m command) build() *cli.Command { Destination: &cfg.nvidiaCTKPath, }, &cli.StringFlag{ - Name: "target-driver-root", - Usage: "Specify the NVIDIA GPU driver root to use in the generated CDI specification. Only used if not empty and differs from 'driver-root' used during discovery.", - Value: "", - Destination: &cfg.targetDriverRoot, + Name: "vendor", + Aliases: []string{"cdi-vendor"}, + Usage: "the vendor string to use for the generated CDI specification.", + Value: "nvidia.com", + Destination: &cfg.vendor, }, &cli.StringFlag{ - Name: "kind", - Aliases: []string{"cdi-kind"}, - Usage: "the vendor string to use for the generated CDI specification.", - Value: "nvidia.com/gpu", - Destination: &cfg.kind, + Name: "class", + Aliases: []string{"cdi-class"}, + Usage: "the class string to use for the generated CDI specification.", + Value: "gpu", + Destination: &cfg.class, }, } @@ -158,10 +156,6 @@ func (m command) validateFlags(c *cli.Context, cfg *config) error { cfg.nvidiaCTKPath = discover.FindNvidiaCTK(m.logger, cfg.nvidiaCTKPath) - if cfg.targetDriverRoot == "" { - cfg.targetDriverRoot = cfg.driverRoot - } - if outputFileFormat := formatFromFilename(cfg.output); outputFileFormat != "" { m.logger.Debugf("Inferred output format as %q from output file name", outputFileFormat) if !c.IsSet("format") { @@ -171,16 +165,12 @@ func (m command) validateFlags(c *cli.Context, cfg *config) error { } } - vendor, class := cdi.ParseQualifier(cfg.kind) - if err := cdi.ValidateVendorName(vendor); err != nil { + if err := cdi.ValidateVendorName(cfg.vendor); err != nil { return fmt.Errorf("invalid CDI vendor name: %v", err) } - if err := cdi.ValidateClassName(class); err != nil { + if err := cdi.ValidateClassName(cfg.class); err != nil { return fmt.Errorf("invalid CDI class name: %v", err) } - cfg.vendor = vendor - cfg.class = class - return nil } @@ -255,26 +245,13 @@ func (m command) generateSpec(cfg *config) (spec.Interface, error) { return nil, fmt.Errorf("failed to create edits common for entities: %v", err) } - spec, err := spec.New( + return spec.New( spec.WithVendor(cfg.vendor), spec.WithClass(cfg.class), spec.WithDeviceSpecs(deviceSpecs), spec.WithEdits(*commonEdits.ContainerEdits), spec.WithFormat(cfg.format), ) - if err != nil { - return nil, err - } - - err = transform.NewRootTransformer( - cfg.driverRoot, - cfg.targetDriverRoot, - ).Transform(spec.Raw()) - if err != nil { - return nil, fmt.Errorf("failed to transform driver root in CDI spec: %v", err) - } - - return spec, err } // MergeDeviceSpecs creates a device with the specified name which combines the edits from the previous devices. From 7f7fc35843dcdebb8addafec5cbfccdf343a8bbc Mon Sep 17 00:00:00 2001 From: Evan Lezar Date: Tue, 28 Mar 2023 21:09:20 +0200 Subject: [PATCH 5/5] Move input and output to transform root subcommand Signed-off-by: Evan Lezar --- cmd/nvidia-ctk/cdi/transform/root/root.go | 87 ++++++++++++++++++----- cmd/nvidia-ctk/cdi/transform/transform.go | 81 +-------------------- 2 files changed, 73 insertions(+), 95 deletions(-) diff --git a/cmd/nvidia-ctk/cdi/transform/root/root.go b/cmd/nvidia-ctk/cdi/transform/root/root.go index 6014cea8f..b89a6c156 100644 --- a/cmd/nvidia-ctk/cdi/transform/root/root.go +++ b/cmd/nvidia-ctk/cdi/transform/root/root.go @@ -18,9 +18,12 @@ package root import ( "fmt" + "io" + "os" "github.com/NVIDIA/nvidia-container-toolkit/pkg/nvcdi/spec" "github.com/NVIDIA/nvidia-container-toolkit/pkg/nvcdi/transform" + "github.com/container-orchestrated-devices/container-device-interface/pkg/cdi" "github.com/sirupsen/logrus" "github.com/urfave/cli/v2" ) @@ -32,73 +35,125 @@ type loadSaver interface { type command struct { logger *logrus.Logger +} - handler loadSaver +type transformOptions struct { + input string + output string } -type config struct { +type options struct { + transformOptions from string to string } // NewCommand constructs a generate-cdi command with the specified logger -func NewCommand(logger *logrus.Logger, specHandler loadSaver) *cli.Command { +func NewCommand(logger *logrus.Logger) *cli.Command { c := command{ - logger: logger, - handler: specHandler, + logger: logger, } return c.build() } // build creates the CLI command func (m command) build() *cli.Command { - cfg := config{} + opts := options{} c := cli.Command{ Name: "root", Usage: "Apply a root transform to a CDI specification", Before: func(c *cli.Context) error { - return m.validateFlags(c, &cfg) + return m.validateFlags(c, &opts) }, Action: func(c *cli.Context) error { - return m.run(c, &cfg) + return m.run(c, &opts) }, } c.Flags = []cli.Flag{ + &cli.StringFlag{ + Name: "input", + Usage: "Specify the file to read the CDI specification from. If this is '-' the specification is read from STDIN", + Value: "-", + Destination: &opts.input, + }, + &cli.StringFlag{ + Name: "output", + Usage: "Specify the file to output the generated CDI specification to. If this is '' the specification is output to STDOUT", + Destination: &opts.output, + }, &cli.StringFlag{ Name: "from", Usage: "specify the root to be transformed", - Destination: &cfg.from, + Destination: &opts.from, }, &cli.StringFlag{ Name: "to", Usage: "specify the replacement root. If this is the same as the from root, the transform is a no-op.", Value: "", - Destination: &cfg.to, + Destination: &opts.to, }, } return &c } -func (m command) validateFlags(c *cli.Context, cfg *config) error { +func (m command) validateFlags(c *cli.Context, opts *options) error { return nil } -func (m command) run(c *cli.Context, cfg *config) error { - spec, err := m.handler.Load() +func (m command) run(c *cli.Context, opts *options) error { + spec, err := opts.Load() if err != nil { return fmt.Errorf("failed to load CDI specification: %w", err) } err = transform.NewRootTransformer( - cfg.from, - cfg.to, + opts.from, + opts.to, ).Transform(spec.Raw()) if err != nil { return fmt.Errorf("failed to transform CDI specification: %w", err) } - return m.handler.Save(spec) + return opts.Save(spec) +} + +// Load lodas the input CDI specification +func (o transformOptions) Load() (spec.Interface, error) { + contents, err := o.getContents() + if err != nil { + return nil, fmt.Errorf("failed to read spec contents: %v", err) + } + + raw, err := cdi.ParseSpec(contents) + if err != nil { + return nil, fmt.Errorf("failed to parse CDI spec: %v", err) + } + + return spec.New( + spec.WithRawSpec(raw), + ) +} + +func (o transformOptions) getContents() ([]byte, error) { + if o.input == "-" { + return io.ReadAll(os.Stdin) + } + + return os.ReadFile(o.input) +} + +// Save saves the CDI specification to the output file +func (o transformOptions) Save(s spec.Interface) error { + if o.output == "" { + _, err := s.WriteTo(os.Stdout) + if err != nil { + return fmt.Errorf("failed to write CDI spec to STDOUT: %v", err) + } + return nil + } + + return s.Save(o.output) } diff --git a/cmd/nvidia-ctk/cdi/transform/transform.go b/cmd/nvidia-ctk/cdi/transform/transform.go index 22ad2e136..5538166d1 100644 --- a/cmd/nvidia-ctk/cdi/transform/transform.go +++ b/cmd/nvidia-ctk/cdi/transform/transform.go @@ -17,13 +17,7 @@ package transform import ( - "fmt" - "io" - "os" - "github.com/NVIDIA/nvidia-container-toolkit/cmd/nvidia-ctk/cdi/transform/root" - "github.com/NVIDIA/nvidia-container-toolkit/pkg/nvcdi/spec" - "github.com/container-orchestrated-devices/container-device-interface/pkg/cdi" "github.com/sirupsen/logrus" "github.com/urfave/cli/v2" ) @@ -32,11 +26,6 @@ type command struct { logger *logrus.Logger } -type options struct { - input string - output string -} - // NewCommand constructs a command with the specified logger func NewCommand(logger *logrus.Logger) *cli.Command { c := command{ @@ -47,82 +36,16 @@ func NewCommand(logger *logrus.Logger) *cli.Command { // build creates the CLI command func (m command) build() *cli.Command { - opts := options{} - c := cli.Command{ Name: "transform", Usage: "Apply a transform to a CDI specification", - Before: func(c *cli.Context) error { - return m.validateFlags(c, &opts) - }, - Action: func(c *cli.Context) error { - return m.run(c, &opts) - }, } - c.Flags = []cli.Flag{ - &cli.StringFlag{ - Name: "input", - Usage: "Specify the file to read the CDI specification from. If this is '-' the specification is read from STDIN", - Value: "-", - Destination: &opts.input, - }, - &cli.StringFlag{ - Name: "output", - Usage: "Specify the file to output the generated CDI specification to. If this is '' the specification is output to STDOUT", - Destination: &opts.output, - }, - } + c.Flags = []cli.Flag{} c.Subcommands = []*cli.Command{ - root.NewCommand(m.logger, &opts), + root.NewCommand(m.logger), } return &c } - -func (m command) validateFlags(c *cli.Context, opts *options) error { - return nil -} - -func (m command) run(c *cli.Context, cfg *options) error { - return nil -} - -// Load lodas the input CDI specification -func (o options) Load() (spec.Interface, error) { - contents, err := o.getContents() - if err != nil { - return nil, fmt.Errorf("failed to read spec contents: %v", err) - } - - raw, err := cdi.ParseSpec(contents) - if err != nil { - return nil, fmt.Errorf("failed to parse CDI spec: %v", err) - } - - return spec.New( - spec.WithRawSpec(raw), - ) -} - -func (o options) getContents() ([]byte, error) { - if o.input == "-" { - return io.ReadAll(os.Stdin) - } - - return os.ReadFile(o.input) -} - -// Save saves the CDI specification to the output file -func (o options) Save(s spec.Interface) error { - if o.output == "" { - _, err := s.WriteTo(os.Stdout) - if err != nil { - return fmt.Errorf("failed to write CDI spec to STDOUT: %v", err) - } - return nil - } - - return s.Save(o.output) -}