Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 38 additions & 10 deletions alpha/declcfg/write.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,7 @@ import (
// output is sorted lexicographically by package name, and then by channel name
// if provided, minEdgeName will be used as the lower bound for edges in the output graph
//
// NB: Output has wrapper comments stating the skipRange edge caveat in HTML comment format, which cannot be parsed by mermaid renderers.
//
// This is deliberate, and intended as an explicit acknowledgement of the limitations, instead of requiring the user to notice the missing edges upon inspection.
//
// Example output:
// <!-- PLEASE NOTE: skipRange edges are not currently displayed -->
// graph LR
//
// %% package "neuvector-certified-operator-rhmp"
Expand All @@ -40,7 +35,6 @@ import (
// end
//
// end
// <!-- PLEASE NOTE: skipRange edges are not currently displayed -->
func WriteMermaidChannels(cfg DeclarativeConfig, out io.Writer, minEdgeName string) error {
pkgs := map[string]*strings.Builder{}

Expand All @@ -59,6 +53,15 @@ func WriteMermaidChannels(cfg DeclarativeConfig, out io.Writer, minEdgeName stri
}
}

// build increasing-version-ordered bundle names, so we can meaningfully iterate over a range
orderedBundles := []string{}
for n, _ := range versionMap {
orderedBundles = append(orderedBundles, n)
}
sort.Slice(orderedBundles, func(i, j int) bool {
return versionMap[orderedBundles[i]].LT(versionMap[orderedBundles[j]])
})

for _, c := range cfg.Channels {
filteredChannel := filterChannel(&c, versionMap, minEdgeName)
if filteredChannel != nil {
Expand Down Expand Up @@ -88,13 +91,24 @@ func WriteMermaidChannels(cfg DeclarativeConfig, out io.Writer, minEdgeName stri
pkgBuilder.WriteString(fmt.Sprintf(" %s[%q]-- %s --> %s[%q]\n", entryId, ce.Name, "skips", skipsId, s))
}
}
if len(ce.SkipRange) > 0 {
skipRange, err := semver.ParseRange(ce.SkipRange)
if err != nil {
return err
}
for _, bundleName := range orderedBundles {
if skipRange(versionMap[bundleName]) {
skipRangeId := fmt.Sprintf("%s-%s", channelID, bundleName)
pkgBuilder.WriteString(fmt.Sprintf(" %s[%q]-- \"%s(%s)\" --> %s[%q]\n", entryId, ce.Name, "skipRange", ce.SkipRange, skipRangeId, bundleName))
}
}
}
}
}
pkgBuilder.WriteString(" end\n")
}
}

out.Write([]byte("<!-- PLEASE NOTE: skipRange edges are not currently displayed -->\n"))
out.Write([]byte("graph LR\n"))
pkgNames := []string{}
for pname, _ := range pkgs {
Expand All @@ -109,7 +123,6 @@ func WriteMermaidChannels(cfg DeclarativeConfig, out io.Writer, minEdgeName stri
out.Write([]byte(pkgs[pkgName].String()))
out.Write([]byte(" end\n"))
}
out.Write([]byte("<!-- PLEASE NOTE: skipRange edges are not currently displayed -->\n"))

return nil
}
Expand All @@ -121,6 +134,20 @@ func filterChannel(c *Channel, versionMap map[string]semver.Version, minEdgeName
if minEdgeName == "" {
return c
}

// short-circuit where the minEdgeName is not in this channel
edgeNames := make(map[string]struct{})

for _, ce := range c.Entries {
if _, ok := edgeNames[ce.Name]; !ok {
edgeNames[ce.Name] = struct{}{}
}
}

if _, ok := edgeNames[minEdgeName]; !ok {
return nil
}

// convert the edge name to the version so we don't have to duplicate the lookup
minVersion := versionMap[minEdgeName]

Expand All @@ -132,8 +159,9 @@ func filterChannel(c *Channel, versionMap map[string]semver.Version, minEdgeName
out.Entries = append(out.Entries, filteredCe)
continue
}
// if len(ce.SkipRange) > 0 {
// }
if len(ce.SkipRange) > 0 {
filteredCe.SkipRange = ce.SkipRange
}
if len(ce.Replaces) > 0 {
if versionMap[ce.Replaces].GTE(minVersion) {
filteredCe.Replaces = ce.Replaces
Expand Down
39 changes: 29 additions & 10 deletions alpha/declcfg/write_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -472,16 +472,17 @@ func removeJSONWhitespace(cfg *DeclarativeConfig) {

func TestWriteMermaidChannels(t *testing.T) {
type spec struct {
name string
cfg DeclarativeConfig
expected string
name string
cfg DeclarativeConfig
startEdge string
expected string
}
specs := []spec{
{
name: "Success",
cfg: buildValidDeclarativeConfig(true),
expected: `<!-- PLEASE NOTE: skipRange edges are not currently displayed -->
graph LR
name: "SuccessNoFilters",
cfg: buildValidDeclarativeConfig(true),
startEdge: "",
expected: `graph LR
%% package "anakin"
subgraph "anakin"
%% channel "dark"
Expand Down Expand Up @@ -509,15 +510,33 @@ graph LR
boba-fett-mando-boba-fett.v2.0.0["boba-fett.v2.0.0"]-- replaces --> boba-fett-mando-boba-fett.v1.0.0["boba-fett.v1.0.0"]
end
end
<!-- PLEASE NOTE: skipRange edges are not currently displayed -->
`,
},
{
name: "SuccessMinEdgeFilter",
cfg: buildValidDeclarativeConfig(true),
startEdge: "anakin.v0.1.0",
expected: `graph LR
%% package "anakin"
subgraph "anakin"
%% channel "dark"
subgraph anakin-dark["dark"]
anakin-dark-anakin.v0.1.0["anakin.v0.1.0"]
anakin-dark-anakin.v0.1.1["anakin.v0.1.1"]
anakin-dark-anakin.v0.1.1["anakin.v0.1.1"]-- skips --> anakin-dark-anakin.v0.1.0["anakin.v0.1.0"]
end
%% channel "light"
subgraph anakin-light["light"]
anakin-light-anakin.v0.1.0["anakin.v0.1.0"]
end
end
`,
},
}
startVersion := ""
for _, s := range specs {
t.Run(s.name, func(t *testing.T) {
var buf bytes.Buffer
err := WriteMermaidChannels(s.cfg, &buf, startVersion)
err := WriteMermaidChannels(s.cfg, &buf, s.startEdge)
require.NoError(t, err)
require.Equal(t, s.expected, buf.String())
})
Expand Down