blob: fcc8fdfe9675dd6c3be4bbbc144de4ffb95fe0d9 [file] [log] [blame]
package packagediff
import (
"fmt"
"cos.googlesource.com/cos/tools.git/src/cmd/cos_image_analyzer/internal/input"
)
// PkgDiff is used to hold package difference between the two images
type PkgDiff struct {
category []string
name []string
version []string
revision []string
typeOFDiff string // "image1" or "image2" if package is unique to image1 or image2, "shared" if package is shared in both images
}
// Differences is an intermediate struct used to store package lists and differences
type Differences struct {
PackageDiff []PkgDiff // If two images are passed in, this is a slice of all package differences
PackageList []Package // If only one image is passed in, return full package list
}
// searchPackageList determines whether a package name appears in a package list
func searchPackageList(packageName string, packageList []Package) (Package, bool) {
for _, p := range packageList {
if p.Name == packageName {
return p, true
}
}
return Package{}, false
}
// packageListDiff calculates the package list difference the two images
// Input:
// ([]Package) packagesImage1 - Image1's package list
// ([]Package) packagesImage2 - Image2's package list
// (*FlagInfo) flagInfo - A struct that holds input preference from the user
// Output: nil on success, else err
func (d *Differences) packageListDiff(packagesImage1, packagesImage2 []Package, flagInfo *input.FlagInfo) error {
if flagInfo.Image2 != "" {
for _, p1 := range packagesImage1 {
pkdDiff := PkgDiff{}
p2, ok := searchPackageList(p1.Name, packagesImage2)
if !ok { // Unique package to image 1
pkdDiff.typeOFDiff = "image1"
pkdDiff.name = []string{p1.Name}
pkdDiff.category = []string{p1.Category}
pkdDiff.version = []string{p1.Version}
pkdDiff.revision = []string{p1.Revision}
d.PackageDiff = append(d.PackageDiff, pkdDiff)
} else { // Shared package to image1 and image2
if p1.Category != p2.Category {
pkdDiff.category = []string{p1.Category, p2.Category}
}
if p1.Version != p2.Version {
pkdDiff.version = []string{p1.Version, p2.Version}
}
if p1.Revision != p2.Revision {
pkdDiff.revision = []string{p1.Revision, p2.Revision}
}
if len(pkdDiff.category) == 2 || len(pkdDiff.version) == 2 || len(pkdDiff.revision) == 2 {
pkdDiff.typeOFDiff = "shared"
pkdDiff.name = []string{p1.Name, p2.Name}
d.PackageDiff = append(d.PackageDiff, pkdDiff)
}
}
}
for _, p2 := range packagesImage2 {
pkdDiff := PkgDiff{}
if _, ok := searchPackageList(p2.Name, packagesImage1); !ok { // Unique package to image2
pkdDiff.typeOFDiff = "image2"
pkdDiff.category = []string{p2.Category}
pkdDiff.name = []string{p2.Name}
pkdDiff.version = []string{p2.Version}
pkdDiff.revision = []string{p2.Revision}
d.PackageDiff = append(d.PackageDiff, pkdDiff)
}
}
} else {
d.PackageList = packagesImage1
}
return nil
}
// FormatPackageListDiff returns a formated string of the package list difference
// (string) image1 - Temp directory name of image1
// (string) image2 - Temp directory name of image2
func (d *Differences) FormatPackageListDiff(image1, image2 string) string {
if len(d.PackageList) > 0 { // One image is passed in, return full package list
pkgList := ""
for _, p := range d.PackageList {
pkgStr := "Package " + p.Name + "\n" + "category: " + p.Category + "\n" + "version: " + p.Version + "\n" + "revision: " + p.Revision + "\n\n"
pkgList += pkgStr
}
return pkgList
} else if len(d.PackageDiff) > 0 { // Two images are passed in, compare based on Differences
pkgDiff := ""
for _, pd := range d.PackageDiff {
pkgStr := ""
if pd.typeOFDiff == "shared" { // Compare shared packages
if len(pd.category) == 2 {
pkgStr += "category:\n" + "< " + pd.category[0] + "\n" + "> " + pd.category[1] + "\n"
}
if len(pd.version) == 2 {
pkgStr += "version:\n" + "< " + pd.version[0] + "\n" + "> " + pd.version[1] + "\n"
}
if len(pd.revision) == 2 {
pkgStr += "revision:\n" + "< " + pd.revision[0] + "\n" + "> " + pd.revision[1] + "\n"
}
if pkgStr != "" && len(pd.name) == 2 {
pkgStr = "Package " + pd.name[0] + " in " + image1 + " and " + image2 + " differ\n" + pkgStr + "\n"
pkgDiff += pkgStr
}
} else { // Unique package, return all info
if len(pd.name) == 1 && len(pd.category) == 1 && len(pd.version) == 1 && len(pd.revision) == 1 {
if pd.typeOFDiff == "image1" {
pkgStr += "Only in " + image1
} else if pd.typeOFDiff == "image2" {
pkgStr += "Only in " + image2
}
pkgStr += ": " + pd.name[0] + "\ncategory: " + pd.category[0] + "\nversion: " + pd.version[0] + "\nrevision: " + pd.revision[0] + "\n\n"
pkgDiff += pkgStr
}
}
}
return pkgDiff
}
return ""
}
// Diff is a tool that finds all package differences of two COS images
// (Category, Name, Version, Revision)
// Input:
// (*ImageInfo) image1 - A struct that will store package info for image1
// (*ImageInfo) image2 - A struct that will store package info for image2
// (*FlagInfo) flagInfo - A struct that holds input preference from the user
// Output:
// (*Differences) packageDiff - A struct that will store the package differences
func Diff(packagesImage1, packagesImage2 []Package, flagInfo *input.FlagInfo) (*Differences, error) {
packageDiff := &Differences{}
if flagInfo.PackageSelected {
if err := packageDiff.packageListDiff(packagesImage1, packagesImage2, flagInfo); err != nil {
return packageDiff, fmt.Errorf("failed to take package list difference: %v", err)
}
}
return packageDiff, nil
}