diff --git a/README.md b/README.md index 1a5d8e8..c73dc66 100644 --- a/README.md +++ b/README.md @@ -15,8 +15,7 @@ Under construction! Parameters: - `rancher_version`: The version of Rancher that is used for querying KDM data, examples: v2.5.10, v2.6.3, v2.6.0 -- `channel`: The source of KDM data used, valid options are: `embedded` (what is in the released version, no out-of-band data), `release` (what is currently released in KDM and active to installs refreshing KDM from online source), `dev` (what is currently in development) -- `channel_version`: The version for the source of KDM data used (KDM is published for each minor Rancher version), examples: v2.5, v2.6 +- `channel`: The source of KDM data used, valid options are: `embedded` (what is in the released version, no out-of-band data), `release` (what is currently released in KDM and active to installs refreshing KDM from online source), `dev` (what is currently in development), `./$FILE` (local data file, must be prefixed with `./` to indicate local data file) ## Examples @@ -47,13 +46,19 @@ kdmq --verbose diffk8s v2.6.3 v2.6.3 embedded release * List k8s images for a Rancher k8s version ``` -kdmq listk8simages v1.22.5-rancher1-1 v2.6 release +kdmq listk8simages v1.22.5-rancher1-1 v2.6.3 release ``` * Diff k8s images for Rancher k8s versions ``` -kdmq diffk8simages v1.22.5-rancher1-1 v1.22.4-rancher1-1 v2.6 dev +kdmq diffk8simages v1.22.5-rancher1-1 v1.22.4-rancher1-1 v2.6.3 dev release +``` + +* Diff k8s images for Rancher k8s versions with local data + +``` +kdmq diffk8simages v1.22.5-rancher1-1 v1.22.4-rancher1-1 v2.6.3 dev ./data.json ``` * Diff oneway all k8s images between Rancher k8s versions between Rancher version @@ -65,11 +70,11 @@ kdmq --diff-oneway diffallk8simages v2.6.0 v2.6.3 embedded embedded * List k8s addons for Rancher k8s version ``` -kdmq listk8saddons v1.22.5-rancher1-1 v2.6 release +kdmq listk8saddons v1.22.5-rancher1-1 v2.6.3 release ``` * Diff k8s addons for Rancher k8s versions ``` -kdmq diffk8saddons v1.22.5-rancher1-1 v1.21.7-rancher1-1 v2.6 release +kdmq diffk8saddons v1.22.5-rancher1-1 v1.21.7-rancher1-1 v2.6.3 release release ``` diff --git a/main.go b/main.go index 233f809..f365002 100644 --- a/main.go +++ b/main.go @@ -34,30 +34,13 @@ func main() { version := c.Args().Get(0) channel := c.Args().Get(1) - validChannel, err := util.IsValidChannel(channel) - if !validChannel { - return fmt.Errorf("Not a valid channel: [%s], error [%v]", channel, err) - } var data kdm.Data + var err error - if channel == "embedded" { - data, err = util.GetKDMDataFromEmbedded(version) - if err != nil { - return fmt.Errorf("Error while trying to get KDM data from embedded, error [%v]", err) - } - } else { - - semVersion, err := util.GetSemverFromString(version) - if err != nil { - return fmt.Errorf("Not a valid semver version: [%s], error [%v]", version, err) - } - - data, err = util.GetKDMDataFromURL(channel, fmt.Sprintf("v%d.%d", semVersion.Major, semVersion.Minor)) - if err != nil { - return fmt.Errorf("Error while trying to get KDM data, error [%v]", err) - } + data, err = util.GetDataForChannel(version, channel) + if err != nil { + return fmt.Errorf("Error while trying to get KDM data, error [%v]", err) } - k8sVersions, err := util.GetK8sVersionsForVersion(data, version) if err != nil { return fmt.Errorf("Error while trying to get k8s versions, error [%v]", err) @@ -98,45 +81,17 @@ func main() { var dataVersion2 kdm.Data var customChannel2 bool - if channel1 == "embedded" { - dataVersion1, err = util.GetKDMDataFromEmbedded(version1) - if err != nil { - return fmt.Errorf("Error while retrieving KDM data for channel [%s], error [%v]", channel1, err) - } - } else { - semVersion1, err := util.GetSemverFromString(version1) - if err != nil { - return fmt.Errorf("Not a valid semver version: [%s], error [%v]", version1, err) - } - - dataVersion1, err = util.GetKDMDataFromURL(channel1, fmt.Sprintf("v%d.%d", semVersion1.Major, semVersion1.Minor)) - if err != nil { - return fmt.Errorf("Error while retrieving KDM data from URL for channel [%s], error [%v]", channel1, err) - } + dataVersion1, err = util.GetDataForChannel(version1, channel1) + if err != nil { + return fmt.Errorf("Error while trying to get KDM data, error [%v]", err) } if channel2 != "" { customChannel2 = true - validChannel, err := util.IsValidChannel(channel2) - if !validChannel { - return fmt.Errorf("Not a valid channel2: [%s], error [%v]", channel2, err) + dataVersion2, err = util.GetDataForChannel(version2, channel2) + if err != nil { + return fmt.Errorf("Error while trying to get KDM data, error [%v]", err) } - if channel2 == "embedded" { - dataVersion2, err = util.GetKDMDataFromEmbedded(version2) - if err != nil { - return fmt.Errorf("Error while retrieving KDM data for channel [%s], error [%v]", channel2, err) - } - } else { - semVersion2, err := util.GetSemverFromString(version2) - if err != nil { - return fmt.Errorf("Not a valid semver version: [%s], error [%v]", version2, err) - } - - dataVersion2, err = util.GetKDMDataFromURL(channel2, fmt.Sprintf("v%d.%d", semVersion2.Major, semVersion2.Minor)) - if err != nil { - return fmt.Errorf("Error while retrieving KDM data from URL for channel [%s], error [%v]", channel2, err) - } - } } var k8sVersionsVersion2 []string @@ -197,58 +152,26 @@ func main() { channel1 := c.Args().Get(2) channel2 := c.Args().Get(3) - validChannel, err := util.IsValidChannel(channel1) - if !validChannel { - return fmt.Errorf("Not a valid channel1: [%s], error [%v]", channel1, err) - } var dataVersion1 kdm.Data var dataVersion2 kdm.Data + var err error - if channel1 == "embedded" { - dataVersion1, err = util.GetKDMDataFromEmbedded(version1) - if err != nil { - return fmt.Errorf("Error while retrieving KDM data for channel [%s], error [%v]", channel1, err) - } - } else { - semVersion1, err := util.GetSemverFromString(version1) - if err != nil { - return fmt.Errorf("Not a valid semver version: [%s], error [%v]", version1, err) - } - - dataVersion1, err = util.GetKDMDataFromURL(channel1, fmt.Sprintf("v%d.%d", semVersion1.Major, semVersion1.Minor)) - if err != nil { - return fmt.Errorf("Error while retrieving KDM data from URL for channel [%s], error [%v]", channel1, err) - } - } - validChannel, err = util.IsValidChannel(channel2) - if !validChannel { - return fmt.Errorf("Not a valid channel2: [%s], error [%v]", channel2, err) + dataVersion1, err = util.GetDataForChannel(version1, channel1) + if err != nil { + return fmt.Errorf("Error while trying to get KDM data, error [%v]", err) } - if channel2 == "embedded" { - dataVersion2, err = util.GetKDMDataFromEmbedded(version2) - if err != nil { - return fmt.Errorf("Error while retrieving KDM data for channel [%s], error [%v]", channel2, err) - } - } else { - semVersion2, err := util.GetSemverFromString(version2) - if err != nil { - return fmt.Errorf("Not a valid semver version: [%s], error [%v]", version2, err) - } - - dataVersion2, err = util.GetKDMDataFromURL(channel2, fmt.Sprintf("v%d.%d", semVersion2.Major, semVersion2.Minor)) - if err != nil { - return fmt.Errorf("Error while retrieving KDM data from URL for channel [%s], error [%v]", channel2, err) - } + dataVersion2, err = util.GetDataForChannel(version2, channel2) + if err != nil { + return fmt.Errorf("Error while trying to get KDM data, error [%v]", err) } - var k8sVersionsVersion2 []string k8sVersionsVersion1, err := util.GetK8sVersionsForVersion(dataVersion1, version1) if err != nil { return fmt.Errorf("Error while trying to get k8s versions for [%s], error: [%v]", version1, err) } - k8sVersionsVersion2, err = util.GetK8sVersionsForVersion(dataVersion2, version2) + k8sVersionsVersion2, err := util.GetK8sVersionsForVersion(dataVersion2, version2) if err != nil { return fmt.Errorf("Error while trying to get k8s versions for [%s], error: [%v]", version1, err) } @@ -291,30 +214,31 @@ func main() { Aliases: []string{"lki"}, Usage: "list k8s images", Action: func(c *cli.Context) error { - commandUsage := fmt.Sprintf("Usage: %s ", c.Command.FullName()) + commandUsage := fmt.Sprintf("Usage: %s ", c.Command.FullName()) if c.Args().Len() < 3 { return fmt.Errorf("Not enough parameters\n%s", commandUsage) } k8sVersion := c.Args().Get(0) - channelVersion := c.Args().Get(1) + version := c.Args().Get(1) channel := c.Args().Get(2) - validChannel, err := util.IsValidChannel(channel) - if !validChannel { - return fmt.Errorf("Not a valid channel: [%s], error [%v]", channel, err) - } + var data kdm.Data + var err error - validChannelVersion, err := util.IsValidChannelVersion(channelVersion) - if !validChannelVersion { - return fmt.Errorf("Not a valid channel version: [%s], error [%v]", channelVersion, err) + data, err = util.GetDataForChannel(version, channel) + if err != nil { + return fmt.Errorf("Error while trying to get KDM data, error [%v]", err) } - data, err := util.GetKDMDataFromURL(channel, channelVersion) + k8sVersions, err := util.GetK8sVersionsForVersion(data, version) if err != nil { - return fmt.Errorf("Error while retrieving KDM data, error [%v]", err) + return fmt.Errorf("Error while trying to get k8s versions, error [%v]", err) } uniqueImages := util.GetUniqueSystemImageList(data.K8sVersionRKESystemImages[k8sVersion]) + if len(uniqueImages) == 0 { + return fmt.Errorf("No images found for [%s] and Rancher version [%s] and channel [%s], latest available k8s patch versions: [%s]", k8sVersion, version, channel, strings.Join(k8sVersions, ",")) + } if c.Bool("verbose") { fmt.Printf("Images for Kubernetes version [%s] for channel [%s]:\n\n%s\n", k8sVersion, channel, strings.Join(uniqueImages, "\n")) @@ -330,35 +254,51 @@ func main() { Aliases: []string{"dki"}, Usage: "diff 2 k8s version images", Action: func(c *cli.Context) error { - commandUsage := fmt.Sprintf("Usage: %s ", c.Command.FullName()) - if c.Args().Len() < 4 { + commandUsage := fmt.Sprintf("Usage: %s ", c.Command.FullName()) + if c.Args().Len() < 5 { return fmt.Errorf("Not enough parameters\n%s", commandUsage) } k8sVersion1 := c.Args().Get(0) k8sVersion2 := c.Args().Get(1) - channelVersion := c.Args().Get(2) - channel := c.Args().Get(3) + version := c.Args().Get(2) + channel1 := c.Args().Get(3) + channel2 := c.Args().Get(4) - validChannel, err := util.IsValidChannel(channel) - if !validChannel { - return fmt.Errorf("Not a valid channel: [%s], error [%v]", channel, err) - } + var dataVersion1 kdm.Data + var dataVersion2 kdm.Data + var err error - validChannelVersion, err := util.IsValidChannelVersion(channelVersion) - if !validChannelVersion { - return fmt.Errorf("Not a valid channel version: [%s], error [%v]", channel, err) + dataVersion1, err = util.GetDataForChannel(version, channel1) + if err != nil { + return fmt.Errorf("Error while trying to get KDM data, error [%v]", err) } - - data, err := util.GetKDMDataFromURL(channel, channelVersion) + dataVersion2, err = util.GetDataForChannel(version, channel2) if err != nil { - return fmt.Errorf("Error while retrieving KDM data, error [%v]", err) + return fmt.Errorf("Error while trying to get KDM data, error [%v]", err) } - uniqueImagesK8sVersion1 := util.GetUniqueSystemImageList(data.K8sVersionRKESystemImages[k8sVersion1]) + uniqueImagesK8sVersion1 := util.GetUniqueSystemImageList(dataVersion1.K8sVersionRKESystemImages[k8sVersion1]) lenUniqueImagesK8sVersion1 := len(uniqueImagesK8sVersion1) - uniqueImagesK8sVersion2 := util.GetUniqueSystemImageList(data.K8sVersionRKESystemImages[k8sVersion2]) + uniqueImagesK8sVersion2 := util.GetUniqueSystemImageList(dataVersion2.K8sVersionRKESystemImages[k8sVersion2]) lenUniqueImagesK8sVersion2 := len(uniqueImagesK8sVersion2) + k8sVersions1, err := util.GetK8sVersionsForVersion(dataVersion1, version) + if err != nil { + return fmt.Errorf("Error while trying to get k8s versions, error [%v]", err) + } + + k8sVersions2, err := util.GetK8sVersionsForVersion(dataVersion2, version) + if err != nil { + return fmt.Errorf("Error while trying to get k8s versions, error [%v]", err) + } + + if lenUniqueImagesK8sVersion1 == 0 { + return fmt.Errorf("No images found for [%s] and Rancher version [%s] and channel [%s], latest available k8s patch versions: [%s]", k8sVersion1, version, channel1, strings.Join(k8sVersions1, ",")) + } + if lenUniqueImagesK8sVersion2 == 0 { + return fmt.Errorf("No images found for [%s] and Rancher version [%s] and channel [%s], latest available k8s patch versions: [%s]", k8sVersion2, version, channel2, strings.Join(k8sVersions2, ",")) + } + var diffImages []string if c.Bool("diff-oneway") { diffImages = util.DifferenceOneWay(uniqueImagesK8sVersion2, uniqueImagesK8sVersion1) @@ -366,8 +306,8 @@ func main() { diffImages = util.Difference(uniqueImagesK8sVersion1, uniqueImagesK8sVersion2) } - replyMessage := fmt.Sprintf("Images [%d] for Kubernetes version [%s] for channel [%s]:\n\n%s\n", lenUniqueImagesK8sVersion1, k8sVersion1, channel, strings.Join(uniqueImagesK8sVersion1, "\n")) - replyMessage = fmt.Sprintf("%s\nImages [%d] for Kubernetes version [%s] for channel [%s]:\n\n%s\n", replyMessage, lenUniqueImagesK8sVersion2, k8sVersion2, channel, strings.Join(uniqueImagesK8sVersion2, "\n")) + replyMessage := fmt.Sprintf("Images [%d] for Kubernetes version [%s] for channel [%s]:\n\n%s\n", lenUniqueImagesK8sVersion1, k8sVersion1, channel1, strings.Join(uniqueImagesK8sVersion1, "\n")) + replyMessage = fmt.Sprintf("%s\nImages [%d] for Kubernetes version [%s] for channel [%s]:\n\n%s\n", replyMessage, lenUniqueImagesK8sVersion2, k8sVersion2, channel2, strings.Join(uniqueImagesK8sVersion2, "\n")) replyMessage = fmt.Sprintf("%s\nDifference:\n%s\n", replyMessage, strings.Join(diffImages, "\n")) if c.Bool("verbose") { @@ -384,27 +324,20 @@ func main() { Aliases: []string{"lka"}, Usage: "list k8s version addons", Action: func(c *cli.Context) error { - commandUsage := fmt.Sprintf("Usage: %s ", c.Command.FullName()) + commandUsage := fmt.Sprintf("Usage: %s ", c.Command.FullName()) if c.Args().Len() < 3 { return fmt.Errorf("Not enough parameters\n%s", commandUsage) } k8sVersion := c.Args().Get(0) - channelVersion := c.Args().Get(1) + version := c.Args().Get(1) channel := c.Args().Get(2) - validChannel, err := util.IsValidChannel(channel) - if !validChannel { - return fmt.Errorf("Not a valid channel: [%s], error [%v]", channel, err) - } - - validChannelVersion, err := util.IsValidChannelVersion(channelVersion) - if !validChannelVersion { - return fmt.Errorf("Not a valid channel version: [%s], error [%v]", channel, err) - } + var data kdm.Data + var err error - data, err := util.GetKDMDataFromURL(channel, channelVersion) + data, err = util.GetDataForChannel(version, channel) if err != nil { - return fmt.Errorf("Error while retrieving KDM data, error [%v]", err) + return fmt.Errorf("Error while trying to get KDM data, error [%v]", err) } k8sAddons := util.GetAddonNames(data.K8sVersionedTemplates) @@ -455,36 +388,34 @@ func main() { Aliases: []string{"dka"}, Usage: "diff 2 k8s version addons", Action: func(c *cli.Context) error { - commandUsage := fmt.Sprintf("Usage: %s ", c.Command.FullName()) + commandUsage := fmt.Sprintf("Usage: %s ", c.Command.FullName()) - if c.Args().Len() < 4 { + if c.Args().Len() < 5 { return fmt.Errorf("Not enough parameters\n%s", commandUsage) } k8sVersion1 := c.Args().Get(0) k8sVersion2 := c.Args().Get(1) - channelVersion := c.Args().Get(2) - channel := c.Args().Get(3) + version := c.Args().Get(2) + channel1 := c.Args().Get(3) + channel2 := c.Args().Get(4) - validChannel, err := util.IsValidChannel(channel) - if !validChannel { - return fmt.Errorf("Not a valid channel: [%s], error [%v]", channel, err) - } + var dataVersion1 kdm.Data + var dataVersion2 kdm.Data + var err error - validChannelVersion, err := util.IsValidChannelVersion(channelVersion) - if !validChannelVersion { - return fmt.Errorf("Not a valid channel version: [%s], error [%v]", channel, err) + dataVersion1, err = util.GetDataForChannel(version, channel1) + if err != nil { + return fmt.Errorf("Error while trying to get KDM data, error [%v]", err) } - - data, err := util.GetKDMDataFromURL(channel, channelVersion) + dataVersion2, err = util.GetDataForChannel(version, channel2) if err != nil { - return fmt.Errorf("Error while retrieving KDM data, error [%v]", err) + return fmt.Errorf("Error while trying to get KDM data, error [%v]", err) } + tableString := &strings.Builder{} table := tablewriter.NewWriter(tableString) - if c.Bool("verbose") { - table.SetHeader([]string{"Addon", k8sVersion1, k8sVersion2, "Diff?"}) - } + table.SetHeader([]string{"Addon", k8sVersion1, k8sVersion2, "Diff?"}) table.SetAutoWrapText(false) table.SetAutoFormatHeaders(true) table.SetHeaderAlignment(tablewriter.ALIGN_LEFT) @@ -497,14 +428,14 @@ func main() { table.SetTablePadding("\t") // pad with tabs table.SetNoWhiteSpace(true) - k8sAddons := util.GetAddonNames(data.K8sVersionedTemplates) + k8sAddons := util.GetAddonNames(dataVersion1.K8sVersionedTemplates) for _, addon := range k8sAddons { - templateName1, _, err := util.GetTemplate(data.K8sVersionedTemplates, addon, k8sVersion1) + templateName1, _, err := util.GetTemplate(dataVersion1.K8sVersionedTemplates, addon, k8sVersion1) if err != nil { templateName1 = fmt.Sprintf("Error: %v", err) } - templateName2, _, err := util.GetTemplate(data.K8sVersionedTemplates, addon, k8sVersion2) + templateName2, _, err := util.GetTemplate(dataVersion2.K8sVersionedTemplates, addon, k8sVersion2) if err != nil { templateName2 = fmt.Sprintf("Error: %v", err) } @@ -516,8 +447,13 @@ func main() { } table.Render() + var replyMessage string + if c.Bool("verbose") { + replyMessage = fmt.Sprintf("Data for [%s] for Rancher version [%s] and channel [%s]\n", k8sVersion1, version, channel1) + replyMessage = fmt.Sprintf("%sData for [%s] for Rancher version [%s] and channel [%s]\n", replyMessage, k8sVersion2, version, channel2) + } - replyMessage := fmt.Sprintf("%s", tableString.String()) + replyMessage = fmt.Sprintf("%s%s", replyMessage, tableString.String()) fmt.Printf(replyMessage) return nil diff --git a/util/util.go b/util/util.go index 6c3d070..a1910e4 100644 --- a/util/util.go +++ b/util/util.go @@ -3,6 +3,7 @@ package util import ( "fmt" "io/ioutil" + "os" "reflect" "regexp" "sort" @@ -108,6 +109,18 @@ func GetRKE2K8sVersionsForVersion(data kdm.Data, version string) ([]string, erro return RKE2K8sVersions, nil } +func GetKDMDataFromFile(file string) (kdm.Data, error) { + b, err := ioutil.ReadFile(file) + if err != nil { + return kdm.Data{}, fmt.Errorf("Error while trying to read file [%s], error: %v", file, err) + } + data, err := kdm.FromData(b) + if err != nil { + return kdm.Data{}, fmt.Errorf("error translating file data to KDM data, error: %v", err) + } + return data, nil +} + func GetKDMDataFromURL(channel, channelVersion string) (kdm.Data, error) { metadataURL := fmt.Sprintf("https://releases.rancher.com/kontainer-driver-metadata/%s-%s/data.json", channel, channelVersion) retryClient := retryablehttp.NewClient() @@ -245,6 +258,7 @@ func GetAddonNames(data map[string]map[string]string) []string { } k8sAddons = append(k8sAddons, addon) } + sort.Strings(k8sAddons) return k8sAddons } @@ -265,3 +279,57 @@ func GetTemplate(data map[string]map[string]string, templateName, k8sVersion str } return "", "", fmt.Errorf("no %s template found for k8sVersion %s", templateName, k8sVersion) } + +func FileExists(path string) (bool, error) { + _, err := os.Stat(path) + if err == nil { + return true, nil + } + if os.IsNotExist(err) { + return false, nil + } + return false, err +} + +func GetDataForChannel(version, channel string) (kdm.Data, error) { + var data kdm.Data + if strings.HasPrefix(channel, "./") { + fileExists, err := FileExists(channel) + if err != nil { + return data, fmt.Errorf("Error checking local data file: [%s], error [%v]", channel, err) + } + if !fileExists { + return data, fmt.Errorf("Local data file [%s] does not exist", channel) + } + data, err = GetKDMDataFromFile(channel) + if err != nil { + return data, fmt.Errorf("Error while trying to get KDM data from local data file, error [%v]", err) + } + return data, nil + } else { + validChannel, err := IsValidChannel(channel) + if !validChannel { + return data, fmt.Errorf("Not a valid channel: [%s], error [%v]", channel, err) + } + + if channel == "embedded" { + data, err = GetKDMDataFromEmbedded(version) + if err != nil { + return data, fmt.Errorf("Error while trying to get KDM data from embedded, error [%v]", err) + } + return data, nil + } else { + + semVersion, err := GetSemverFromString(version) + if err != nil { + return data, fmt.Errorf("Not a valid semver version: [%s], error [%v]", version, err) + } + + data, err = GetKDMDataFromURL(channel, fmt.Sprintf("v%d.%d", semVersion.Major, semVersion.Minor)) + if err != nil { + return data, fmt.Errorf("Error while trying to get KDM data, error [%v]", err) + } + return data, nil + } + } +}