diff --git a/cmd/application.go b/cmd/application.go index dd3b972..afaf0a8 100644 --- a/cmd/application.go +++ b/cmd/application.go @@ -36,6 +36,6 @@ var applicationsCmd = &cobra.Command{ } func init() { - applicationsCmd.Flags().AddFlagSet(source.FlagSet()) - rootCmd.AddCommand(applicationsCmd) + applicationsCmd.Flags().AddFlagSet(source.AllFlagSets()) + RootCmd.AddCommand(applicationsCmd) } diff --git a/cmd/devices.go b/cmd/devices.go index 9306160..b3c0e12 100644 --- a/cmd/devices.go +++ b/cmd/devices.go @@ -34,6 +34,6 @@ var devicesCmd = &cobra.Command{ } func init() { - devicesCmd.Flags().AddFlagSet(source.FlagSet()) - rootCmd.AddCommand(devicesCmd) + devicesCmd.Flags().AddFlagSet(source.AllFlagSets()) + RootCmd.AddCommand(devicesCmd) } diff --git a/cmd/root.go b/cmd/root.go index b27d8c6..c5f28aa 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -29,7 +29,7 @@ var ( ctx context.Context rootCfg = &source.RootConfig ExportCfg = ExportConfig{} - rootCmd = &cobra.Command{ + RootCmd = &cobra.Command{ Use: "ttn-lw-migrate", Short: "Migrate from other LoRaWAN network servers to The Things Stack", @@ -60,7 +60,7 @@ var ( // Execute runs the root command and returns the exit code. func Execute() int { - if err := rootCmd.Execute(); err != nil { + if err := RootCmd.Execute(); err != nil { printStack(os.Stderr, err) return 1 } @@ -68,15 +68,15 @@ func Execute() int { } func init() { - rootCmd.PersistentFlags().BoolVar(&rootCfg.DryRun, + RootCmd.PersistentFlags().BoolVar(&rootCfg.DryRun, "dry-run", false, "Do everything except resetting root and session keys of exported devices") - rootCmd.PersistentFlags().BoolVar(&rootCfg.Verbose, + RootCmd.PersistentFlags().BoolVar(&rootCfg.Verbose, "verbose", false, "Verbose output") - rootCmd.PersistentFlags().StringVar(&rootCfg.FrequencyPlansURL, + RootCmd.PersistentFlags().StringVar(&rootCfg.FrequencyPlansURL, "frequency-plans-url", "https://raw.githubusercontent.com/TheThingsNetwork/lorawan-frequency-plans/master", "URL for fetching frequency plans") diff --git a/cmd/sources.go b/cmd/sources.go index 2ba4e86..ec1badb 100644 --- a/cmd/sources.go +++ b/cmd/sources.go @@ -34,5 +34,5 @@ var sourcesCmd = &cobra.Command{ } func init() { - rootCmd.AddCommand(sourcesCmd) + RootCmd.AddCommand(sourcesCmd) } diff --git a/cmd/ttn-lw-migrate/main.go b/cmd/ttn-lw-migrate/main.go index b55f7c3..c0fc206 100644 --- a/cmd/ttn-lw-migrate/main.go +++ b/cmd/ttn-lw-migrate/main.go @@ -22,6 +22,7 @@ import ( _ "go.thethings.network/lorawan-stack-migrate/pkg/source/ttnv3" // TTS source "go.thethings.network/lorawan-stack-migrate/cmd" + _ "go.thethings.network/lorawan-stack-migrate/cmd/ttnv3" ) func main() { diff --git a/cmd/ttnv3/application.go b/cmd/ttnv3/application.go new file mode 100644 index 0000000..5d3c4f8 --- /dev/null +++ b/cmd/ttnv3/application.go @@ -0,0 +1,43 @@ +// Copyright © 2023 The Things Network Foundation, The Things Industries B.V. +// +// 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 ttnv3 + +import ( + "github.com/spf13/cobra" + root "go.thethings.network/lorawan-stack-migrate/cmd" + "go.thethings.network/lorawan-stack-migrate/pkg/commands" + "go.thethings.network/lorawan-stack-migrate/pkg/source" +) + +var applicationsCmd = &cobra.Command{ + Use: "application [app-id] ...", + Aliases: []string{"applications", "app"}, + Short: "Export all devices of an application", + Run: func(cmd *cobra.Command, args []string) { + commands.Export(cmd, args, func(s source.Source, item string) error { + return s.RangeDevices(item, root.ExportCfg.ExportDev) + }) + }, +} + +func init() { + ttnv3Cmd.AddCommand(applicationsCmd) + + fs, err := source.FlagSet(sourceName) + if err != nil { + panic(err) + } + applicationsCmd.Flags().AddFlagSet(fs) +} diff --git a/cmd/ttnv3/devices.go b/cmd/ttnv3/devices.go new file mode 100644 index 0000000..2a8c1c5 --- /dev/null +++ b/cmd/ttnv3/devices.go @@ -0,0 +1,41 @@ +// Copyright © 2023 The Things Network Foundation, The Things Industries B.V. +// +// 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 ttnv3 + +import ( + "fmt" + + "github.com/spf13/cobra" + "go.thethings.network/lorawan-stack-migrate/pkg/source" +) + +var devicesCmd = &cobra.Command{ + Use: "device [dev-id] ...", + Short: "Export devices by DevEUI", + Aliases: []string{"end-devices", "end-device", "devices", "dev"}, + Run: func(cmd *cobra.Command, args []string) { + fmt.Printf("%s called\n", cmd.Name()) + }, +} + +func init() { + ttnv3Cmd.AddCommand(devicesCmd) + + fs, err := source.FlagSet(sourceName) + if err != nil { + panic(err) + } + devicesCmd.Flags().AddFlagSet(fs) +} diff --git a/cmd/ttnv3/ttnv3.go b/cmd/ttnv3/ttnv3.go new file mode 100644 index 0000000..4a5943b --- /dev/null +++ b/cmd/ttnv3/ttnv3.go @@ -0,0 +1,33 @@ +// Copyright © 2023 The Things Network Foundation, The Things Industries B.V. +// +// 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 ttnv3 + +import ( + "github.com/spf13/cobra" + "go.thethings.network/lorawan-stack-migrate/cmd" +) + +const sourceName = "ttnv3" + +// ttnv3Cmd represents the ttnv3 source +var ttnv3Cmd = &cobra.Command{ + Use: sourceName + " ...", + Short: "Export devices from The Things Stack", + SilenceUsage: true, +} + +func init() { + cmd.RootCmd.AddCommand(ttnv3Cmd) +} diff --git a/cmd/version.go b/cmd/version.go index 103239e..5e56578 100644 --- a/cmd/version.go +++ b/cmd/version.go @@ -45,5 +45,5 @@ var versionCmd = &cobra.Command{ } func init() { - rootCmd.AddCommand(versionCmd) + RootCmd.AddCommand(versionCmd) } diff --git a/pkg/source/source.go b/pkg/source/source.go index fd4e530..b585f45 100644 --- a/pkg/source/source.go +++ b/pkg/source/source.go @@ -93,24 +93,37 @@ func addPrefix(name, prefix string) string { return prefix + name } -// FlagSet returns flags for all configured sources. -func FlagSet() *pflag.FlagSet { - flags := &pflag.FlagSet{} - names := []string{} +// AllFlagSets returns flags for all configured sources prefixed with source names. +func AllFlagSets() *pflag.FlagSet { + fs := new(pflag.FlagSet) for _, r := range registeredSources { - if r.FlagSet != nil { - r.FlagSet.VisitAll(func(f *pflag.Flag) { - f.Name = addPrefix(f.Name, r.Name) - }) - flags.AddFlagSet(r.FlagSet) - names = append(names, r.Name) + if r.FlagSet == nil { + continue } + r.FlagSet.VisitAll(func(a *pflag.Flag) { + b := *a // Avoid modifying source flags + b.Name = addPrefix(b.Name, r.Name) + fs.AddFlag(&b) + }) } - flags.StringVar(&RootConfig.Source, + fs.StringVar(&RootConfig.Source, "source", "", - fmt.Sprintf("source (%s)", strings.Join(Names(), "|"))) - return flags + fmt.Sprintf("source (%s)", strings.Join(Names(), "|")), + ) + return fs +} + +// FlagSet returns a flag set for a given source. +func FlagSet(s string) (*pflag.FlagSet, error) { + src, ok := registeredSources[s] + if !ok { + return nil, errNoSource.New() + } + if src.FlagSet == nil { + return nil, errNoSource.New() + } + return src.FlagSet, nil } // Sources returns a map of registered Sources and their descriptions.