func Diff()

in pkg/bundle/compare/diff.go [55:206]


func Diff(src, dst *bundlev1.Bundle) ([]DiffItem, error) {
	// Check arguments
	if src == nil {
		return nil, fmt.Errorf("unable to diff with a nil source")
	}
	if dst == nil {
		return nil, fmt.Errorf("unable to diff with a nil destination")
	}

	diffs := []DiffItem{}

	// Index source packages
	srcIndex := map[string]*bundlev1.Package{}
	for _, srcPkg := range src.Packages {
		if srcPkg == nil || srcPkg.Secrets == nil {
			continue
		}

		srcIndex[srcPkg.Name] = srcPkg
	}

	// Index destination packages
	dstIndex := map[string]*bundlev1.Package{}
	for _, dstPkg := range dst.Packages {
		if dstPkg == nil || dstPkg.Secrets == nil {
			continue
		}

		dstIndex[dstPkg.Name] = dstPkg
		if _, ok := srcIndex[dstPkg.Name]; !ok {
			// Package has been added
			diffs = append(diffs, DiffItem{
				Operation: Add,
				Type:      "package",
				Path:      dstPkg.Name,
			})

			// Add keys
			for _, s := range dstPkg.Secrets.Data {
				if s == nil {
					continue
				}

				// Unpack secret value
				var data string
				if err := secret.Unpack(s.Value, &data); err != nil {
					return nil, fmt.Errorf("unable to unpack '%s' - '%s' secret value: %w", dstPkg.Name, s.Key, err)
				}

				diffs = append(diffs, DiffItem{
					Operation: Add,
					Type:      "secret",
					Path:      fmt.Sprintf("%s#%s", dstPkg.Name, s.Key),
					Value:     data,
				})
			}
		}
	}

	// Compute package changes
	for n, sp := range srcIndex {
		dp, ok := dstIndex[n]
		if !ok {
			// Not exist in destination bundle
			diffs = append(diffs, DiffItem{
				Operation: Remove,
				Type:      "package",
				Path:      sp.Name,
			})
			continue
		}

		// Index secret data
		srcSecretIndex := map[string]*bundlev1.KV{}
		for _, ss := range sp.Secrets.Data {
			if ss == nil {
				continue
			}
			srcSecretIndex[ss.Key] = ss
		}
		dstSecretIndex := map[string]*bundlev1.KV{}
		for _, ds := range dp.Secrets.Data {
			if ds == nil {
				continue
			}

			dstSecretIndex[ds.Key] = ds
			oldValue, ok := srcSecretIndex[ds.Key]
			if !ok {
				// Secret has been added
				var data string
				if err := secret.Unpack(ds.Value, &data); err != nil {
					return nil, fmt.Errorf("unable to unpack '%s' - '%s' secret value: %w", dp.Name, ds.Key, err)
				}

				diffs = append(diffs, DiffItem{
					Operation: Add,
					Type:      "secret",
					Path:      fmt.Sprintf("%s#%s", dp.Name, ds.Key),
					Value:     data,
				})
				continue
			}

			// Skip if key does not match
			if !strings.EqualFold(oldValue.Key, ds.Key) {
				continue
			}

			// Compare values
			if !security.SecureCompare(oldValue.Value, ds.Value) {
				// Secret has been replaced
				var data string
				if err := secret.Unpack(ds.Value, &data); err != nil {
					return nil, fmt.Errorf("unable to unpack '%s' - '%s' secret value: %w", dp.Name, ds.Key, err)
				}

				diffs = append(diffs, DiffItem{
					Operation: Replace,
					Type:      "secret",
					Path:      fmt.Sprintf("%s#%s", dp.Name, ds.Key),
					Value:     data,
				})
			}
		}

		// Clean removed source secrets
		for k := range srcSecretIndex {
			if _, ok := dstSecretIndex[k]; !ok {
				diffs = append(diffs, DiffItem{
					Operation: Remove,
					Type:      "secret",
					Path:      fmt.Sprintf("%s#%s", dp.Name, k),
				})
			}
		}
	}

	// Sort diff
	sort.SliceStable(diffs, func(i, j int) bool {
		var (
			x = diffs[i]
			y = diffs[j]
		)

		// Sort by patch descending
		return x.Path < y.Path
	})

	// No error
	return diffs, nil
}