Repository: replicatedhq/kots
Pull request #288 (2020-01-29 21:16:52)

Adding kotsadm to default kots.io collector spec


Patch:
diff --git a/ffi/archive.go b/ffi/archive.go
index 4cd88f99d..ca7e56c4c 100644
--- a/ffi/archive.go
+++ b/ffi/archive.go
@@ -7,7 +7,6 @@ import (
 	"github.com/mholt/archiver"
 	"github.com/pkg/errors"
 	kotsv1beta1 "github.com/replicatedhq/kots/kotskinds/apis/kots/v1beta1"
-	"github.com/replicatedhq/kots/pkg/upstream"
 	"k8s.io/client-go/kubernetes/scheme"
 )
 
@@ -25,18 +24,15 @@ func extractArchive(rootPath, fromArchivePath string) (*archiver.TarGz, error) {
 	return tarGz, nil
 }
 
-func readCursorFromPath(installationFilePath string) (upstream.ReplicatedCursor, error) {
+func readCursorFromPath(installationFilePath string) (string, error) {
 	installation, err := loadInstallationFromPath(installationFilePath)
 	if err != nil {
 		if os.IsNotExist(err) {
-			return upstream.ReplicatedCursor{}, nil
+			return "", nil
 		}
-		return upstream.ReplicatedCursor{}, errors.Wrap(err, "failed to read installation file")
+		return "", errors.Wrap(err, "failed to read installation file")
 	}
-	return upstream.ReplicatedCursor{
-		ChannelName: installation.Spec.ChannelName,
-		Cursor:      installation.Spec.UpdateCursor,
-	}, nil
+	return installation.Spec.UpdateCursor, nil
 }
 
 func loadInstallationFromPath(installationFilePath string) (*kotsv1beta1.Installation, error) {
diff --git a/ffi/main.go b/ffi/main.go
index 95a01927d..9b72ff801 100644
--- a/ffi/main.go
+++ b/ffi/main.go
@@ -88,7 +88,7 @@ func UpdateCheck(socket, fromArchivePath, namespace string) {
 
 		fmt.Printf("Result of checking for updates for %s: Before: %s, After %s\n", license.Spec.AppSlug, beforeCursor, afterCursor)
 
-		isUpdateAvailable := !beforeCursor.Equal(afterCursor)
+		isUpdateAvailable := string(beforeCursor) != string(afterCursor)
 		if !isUpdateAvailable {
 			ffiResult = NewFFIResult(0)
 			return
diff --git a/ffi/output.go b/ffi/output.go
index 49f5b379a..c0a9cb3ab 100644
--- a/ffi/output.go
+++ b/ffi/output.go
@@ -9,7 +9,6 @@ import (
 	"net"
 
 	"github.com/pkg/errors"
-	"github.com/replicatedhq/kots/pkg/util"
 )
 
 type statusMessage struct {
@@ -106,12 +105,7 @@ func NewFFIResult(exitCode int) *FFIResult {
 }
 
 func (f *FFIResult) WithError(err error) *FFIResult {
-	cause := errors.Cause(err)
-	if _, ok := cause.(util.ActionableError); ok {
-		f.Err = cause
-	} else {
-		f.Err = err
-	}
+	f.Err = err
 	return f
 }
 
diff --git a/ffi/updatedownload.go b/ffi/updatedownload.go
index 8c5d977de..3b3b36d14 100644
--- a/ffi/updatedownload.go
+++ b/ffi/updatedownload.go
@@ -109,7 +109,7 @@ func UpdateDownload(socket, fromArchivePath, namespace, registryJson, cursor str
 
 		fmt.Printf("Result of checking for updates for %s: Before: %s, After %s\n", license.Spec.AppSlug, beforeCursor, afterCursor)
 
-		isUpdateAvailable := !beforeCursor.Equal(afterCursor)
+		isUpdateAvailable := string(beforeCursor) != string(afterCursor)
 		if !isUpdateAvailable {
 			ffiResult = NewFFIResult(0)
 			return
@@ -216,7 +216,7 @@ func UpdateDownloadFromAirgap(socket, fromArchivePath, namespace, registryJson,
 			ConfigFile:          filepath.Join(tmpRoot, "upstream", "userdata", "config.yaml"),
 			AirgapRoot:          airgapRoot,
 			InstallationFile:    installationFilePath,
-			UpdateCursor:        beforeCursor.Cursor,
+			UpdateCursor:        beforeCursor,
 			RootDir:             tmpRoot,
 			ExcludeKotsKinds:    true,
 			ExcludeAdminConsole: true,
@@ -246,14 +246,14 @@ func UpdateDownloadFromAirgap(socket, fromArchivePath, namespace, registryJson,
 
 		fmt.Printf("Result of checking for updates for %s: Before: %s, After %s\n", license.Spec.AppSlug, beforeCursor, afterCursor)
 
-		bc, err := cursor.NewCursor(string(beforeCursor.Cursor))
+		bc, err := cursor.NewCursor(string(beforeCursor))
 		if err != nil {
 			fmt.Printf("failed to parse current cursor: %s\n", err.Error())
 			ffiResult = NewFFIResult(-1).WithError(err)
 			return
 		}
 
-		ac, err := cursor.NewCursor(string(afterCursor.Cursor))
+		ac, err := cursor.NewCursor(string(afterCursor))
 		if err != nil {
 			fmt.Printf("failed to parse update cursor: %s\n", err.Error())
 			ffiResult = NewFFIResult(-1).WithError(err)
diff --git a/ffi/updateslist.go b/ffi/updateslist.go
index 4f2181814..39cd364ce 100644
--- a/ffi/updateslist.go
+++ b/ffi/updateslist.go
@@ -12,7 +12,7 @@ import (
 )
 
 //export ListUpdates
-func ListUpdates(socket, licenseData, currentCursor, currentChannel string) {
+func ListUpdates(socket, licenseData, currentCursor string) {
 	go func() {
 		var ffiResult *FFIResult
 
@@ -41,10 +41,9 @@ func ListUpdates(socket, licenseData, currentCursor, currentChannel string) {
 		defer os.Remove(licenseFile)
 
 		getUpdatesOptions := pull.GetUpdatesOptions{
-			LicenseFile:    licenseFile,
-			CurrentCursor:  currentCursor,
-			CurrentChannel: currentChannel,
-			Silent:         true,
+			LicenseFile:   licenseFile,
+			CurrentCursor: currentCursor,
+			Silent:        true,
 		}
 
 		updates, err := pull.GetUpdates(fmt.Sprintf("replicated://%s", license.Spec.AppSlug), getUpdatesOptions)
diff --git a/kotskinds/apis/kots/v1beta1/airgap_types.go b/kotskinds/apis/kots/v1beta1/airgap_types.go
index da6652697..4f037af47 100644
--- a/kotskinds/apis/kots/v1beta1/airgap_types.go
+++ b/kotskinds/apis/kots/v1beta1/airgap_types.go
@@ -25,7 +25,6 @@ type AirgapSpec struct {
 	VersionLabel string `json:"versionLabel,omitempty"`
 	ReleaseNotes string `json:"releaseNotes,omitempty"`
 	UpdateCursor string `json:"updateCursor,omitempty"`
-	ChannelName  string `json:"channelName,omitempty"`
 	Signature    []byte `json:"signature,omitempty"`
 }
 
diff --git a/kotskinds/apis/kots/v1beta1/installation_types.go b/kotskinds/apis/kots/v1beta1/installation_types.go
index 6accac912..00dbceaba 100644
--- a/kotskinds/apis/kots/v1beta1/installation_types.go
+++ b/kotskinds/apis/kots/v1beta1/installation_types.go
@@ -23,7 +23,6 @@ import (
 // InstallationSpec defines the desired state of InstallationSpec
 type InstallationSpec struct {
 	UpdateCursor  string `json:"updateCursor,omitempty"`
-	ChannelName   string `json:"channelName,omitempty"`
 	VersionLabel  string `json:"versionLabel,omitempty"`
 	ReleaseNotes  string `json:"releaseNotes,omitempty"`
 	EncryptionKey string `json:"encryptionKey,omitempty"`
diff --git a/pkg/pull/peek.go b/pkg/pull/peek.go
index 748026a8e..61ea709f1 100644
--- a/pkg/pull/peek.go
+++ b/pkg/pull/peek.go
@@ -7,13 +7,12 @@ import (
 )
 
 type GetUpdatesOptions struct {
-	HelmRepoURI    string
-	Namespace      string
-	LocalPath      string
-	LicenseFile    string
-	CurrentCursor  string
-	CurrentChannel string
-	Silent         bool
+	HelmRepoURI   string
+	Namespace     string
+	LocalPath     string
+	LicenseFile   string
+	CurrentCursor string
+	Silent        bool
 }
 
 // GetUpdates will retrieve all later versions of the application specified in upstreamURI
@@ -31,7 +30,6 @@ func GetUpdates(upstreamURI string, getUpdatesOptions GetUpdatesOptions) ([]upst
 	fetchOptions.HelmRepoURI = getUpdatesOptions.HelmRepoURI
 	fetchOptions.LocalPath = getUpdatesOptions.LocalPath
 	fetchOptions.CurrentCursor = getUpdatesOptions.CurrentCursor
-	fetchOptions.CurrentChannel = getUpdatesOptions.CurrentChannel
 
 	if getUpdatesOptions.LicenseFile != "" {
 		license, err := parseLicenseFromFile(getUpdatesOptions.LicenseFile)
diff --git a/pkg/upstream/fetch.go b/pkg/upstream/fetch.go
index a90c3b375..63e73da16 100644
--- a/pkg/upstream/fetch.go
+++ b/pkg/upstream/fetch.go
@@ -22,7 +22,6 @@ type FetchOptions struct {
 	Airgap              *kotsv1beta1.Airgap
 	EncryptionKey       string
 	CurrentCursor       string
-	CurrentChannel      string
 	CurrentVersionLabel string
 }
 
@@ -76,15 +75,9 @@ func pickVersionLabel(fetchOptions *FetchOptions) string {
 	return fetchOptions.CurrentVersionLabel
 }
 
-func pickCursor(fetchOptions *FetchOptions) ReplicatedCursor {
+func pickCursor(fetchOptions *FetchOptions) string {
 	if fetchOptions.Airgap != nil && fetchOptions.Airgap.Spec.UpdateCursor != "" {
-		return ReplicatedCursor{
-			ChannelName: fetchOptions.Airgap.Spec.ChannelName,
-			Cursor:      fetchOptions.Airgap.Spec.UpdateCursor,
-		}
-	}
-	return ReplicatedCursor{
-		ChannelName: fetchOptions.CurrentChannel,
-		Cursor:      fetchOptions.CurrentCursor,
+		return fetchOptions.Airgap.Spec.UpdateCursor
 	}
+	return fetchOptions.CurrentCursor
 }
diff --git a/pkg/upstream/peek.go b/pkg/upstream/peek.go
index 702778755..b7f27a745 100644
--- a/pkg/upstream/peek.go
+++ b/pkg/upstream/peek.go
@@ -34,11 +34,7 @@ func getUpdatesUpstream(upstreamURI string, fetchOptions *FetchOptions) ([]Updat
 		return getUpdatesHelm(u, fetchOptions.HelmRepoURI)
 	}
 	if u.Scheme == "replicated" {
-		cursor := ReplicatedCursor{
-			ChannelName: fetchOptions.CurrentChannel,
-			Cursor:      fetchOptions.CurrentCursor,
-		}
-		return getUpdatesReplicated(u, fetchOptions.LocalPath, cursor, fetchOptions.CurrentVersionLabel, fetchOptions.License)
+		return getUpdatesReplicated(u, fetchOptions.LocalPath, fetchOptions.CurrentCursor, fetchOptions.CurrentVersionLabel, fetchOptions.License, fetchOptions.CurrentCursor)
 	}
 	if u.Scheme == "git" {
 		// return getUpdatesGit(upstreamURI)
diff --git a/pkg/upstream/replicated.go b/pkg/upstream/replicated.go
index 0798abe0e..79290fb09 100644
--- a/pkg/upstream/replicated.go
+++ b/pkg/upstream/replicated.go
@@ -47,17 +47,12 @@ type ReplicatedUpstream struct {
 	Sequence     *int
 }
 
-type ReplicatedCursor struct {
-	ChannelName string
-	Cursor      string
-}
-
 type App struct {
 	Name string
 }
 
 type Release struct {
-	UpdateCursor ReplicatedCursor
+	UpdateCursor string
 	VersionLabel string
 	ReleaseNotes string
 	Manifests    map[string][]byte
@@ -70,18 +65,14 @@ type ChannelRelease struct {
 	CreatedAt       string `json:"createdAt"`
 }
 
-func (this ReplicatedCursor) Equal(other ReplicatedCursor) bool {
-	return this.ChannelName == other.ChannelName && this.Cursor == other.Cursor
-}
-
-func getUpdatesReplicated(u *url.URL, localPath string, currentCursor ReplicatedCursor, versionLabel string, license *kotsv1beta1.License) ([]Update, error) {
+func getUpdatesReplicated(u *url.URL, localPath string, currentCursor, versionLabel string, license *kotsv1beta1.License, channelSequence string) ([]Update, error) {
 	if localPath != "" {
 		parsedLocalRelease, err := readReplicatedAppFromLocalPath(localPath, currentCursor, versionLabel)
 		if err != nil {
 			return nil, errors.Wrap(err, "failed to read replicated app from local path")
 		}
 
-		return []Update{{Cursor: parsedLocalRelease.UpdateCursor.Cursor, VersionLabel: versionLabel}}, nil
+		return []Update{{Cursor: parsedLocalRelease.UpdateCursor, VersionLabel: versionLabel}}, nil
 	}
 
 	// A license file is required to be set for this to succeed
@@ -99,7 +90,7 @@ func getUpdatesReplicated(u *url.URL, localPath string, currentCursor Replicated
 		return nil, errors.Wrap(err, "failed to get successful head response")
 	}
 
-	pendingReleases, err := listPendingChannelReleases(replicatedUpstream, remoteLicense, currentCursor)
+	pendingReleases, err := listPendingChannelReleases(replicatedUpstream, remoteLicense, channelSequence)
 	if err != nil {
 		return nil, errors.Wrap(err, "failed to list replicated app releases")
 	}
@@ -114,7 +105,7 @@ func getUpdatesReplicated(u *url.URL, localPath string, currentCursor Replicated
 	return updates, nil
 }
 
-func downloadReplicated(u *url.URL, localPath string, rootDir string, useAppDir bool, license *kotsv1beta1.License, existingConfigValues *kotsv1beta1.ConfigValues, updateCursor ReplicatedCursor, versionLabel string, cipher *crypto.AESCipher) (*types.Upstream, error) {
+func downloadReplicated(u *url.URL, localPath string, rootDir string, useAppDir bool, license *kotsv1beta1.License, existingConfigValues *kotsv1beta1.ConfigValues, updateCursor, versionLabel string, cipher *crypto.AESCipher) (*types.Upstream, error) {
 	var release *Release
 
 	if localPath != "" {
@@ -124,6 +115,9 @@ func downloadReplicated(u *url.URL, localPath string, rootDir string, useAppDir
 		}
 
 		release = parsedLocalRelease
+		if updateCursor != "" && release.UpdateCursor != updateCursor {
+			return nil, errors.Wrap(err, "release in local path does not match update cursor")
+		}
 	} else {
 		// A license file is required to be set for this to succeed
 		if license == nil {
@@ -205,8 +199,7 @@ func downloadReplicated(u *url.URL, localPath string, rootDir string, useAppDir
 		Name:          application.Name,
 		Files:         files,
 		Type:          "replicated",
-		UpdateCursor:  release.UpdateCursor.Cursor,
-		ChannelName:   license.Spec.ChannelName,
+		UpdateCursor:  release.UpdateCursor,
 		VersionLabel:  release.VersionLabel,
 		ReleaseNotes:  release.ReleaseNotes,
 		EncryptionKey: cipher.ToString(),
@@ -215,7 +208,7 @@ func downloadReplicated(u *url.URL, localPath string, rootDir string, useAppDir
 	return upstream, nil
 }
 
-func (r *ReplicatedUpstream) getRequest(method string, license *kotsv1beta1.License, cursor ReplicatedCursor) (*http.Request, error) {
+func (r *ReplicatedUpstream) getRequest(method string, license *kotsv1beta1.License, channelSequence string) (*http.Request, error) {
 	u, err := url.Parse(license.Spec.Endpoint)
 	if err != nil {
 		return nil, errors.Wrap(err, "failed to parse endpoint from license")
@@ -230,11 +223,7 @@ func (r *ReplicatedUpstream) getRequest(method string, license *kotsv1beta1.Lice
 	if r.Channel != nil {
 		urlPath = path.Join(urlPath, *r.Channel)
 	}
-
-	urlValues := url.Values{}
-	urlValues.Set("channelSequence", cursor.Cursor)
-	urlValues.Add("licenseSequence", fmt.Sprintf("%d", license.Spec.LicenseSequence))
-	url := fmt.Sprintf("%s://%s?%s", u.Scheme, urlPath, urlValues.Encode())
+	url := fmt.Sprintf("%s://%s?channelSequence=%s", u.Scheme, urlPath, channelSequence)
 
 	req, err := http.NewRequest(method, url, nil)
 	if err != nil {
@@ -270,7 +259,7 @@ func parseReplicatedURL(u *url.URL) (*ReplicatedUpstream, error) {
 }
 
 func getSuccessfulHeadResponse(replicatedUpstream *ReplicatedUpstream, license *kotsv1beta1.License) (*kotsv1beta1.License, error) {
-	headReq, err := replicatedUpstream.getRequest("HEAD", license, ReplicatedCursor{})
+	headReq, err := replicatedUpstream.getRequest("HEAD", license, "")
 	if err != nil {
 		return nil, errors.Wrap(err, "failed to create http request")
 	}
@@ -291,7 +280,7 @@ func getSuccessfulHeadResponse(replicatedUpstream *ReplicatedUpstream, license *
 	return license, nil
 }
 
-func readReplicatedAppFromLocalPath(localPath string, localCursor ReplicatedCursor, versionLabel string) (*Release, error) {
+func readReplicatedAppFromLocalPath(localPath, localCursor, versionLabel string) (*Release, error) {
 	release := Release{
 		Manifests:    make(map[string][]byte),
 		UpdateCursor: localCursor,
@@ -328,8 +317,8 @@ func readReplicatedAppFromLocalPath(localPath string, localCursor ReplicatedCurs
 	return &release, nil
 }
 
-func downloadReplicatedApp(replicatedUpstream *ReplicatedUpstream, license *kotsv1beta1.License, cursor ReplicatedCursor) (*Release, error) {
-	getReq, err := replicatedUpstream.getRequest("GET", license, cursor)
+func downloadReplicatedApp(replicatedUpstream *ReplicatedUpstream, license *kotsv1beta1.License, channelSequence string) (*Release, error) {
+	getReq, err := replicatedUpstream.getRequest("GET", license, channelSequence)
 	if err != nil {
 		return nil, errors.Wrap(err, "failed to create http request")
 	}
@@ -339,16 +328,11 @@ func downloadReplicatedApp(replicatedUpstream *ReplicatedUpstream, license *kots
 	}
 	defer getResp.Body.Close()
 
-	if getResp.StatusCode >= 300 {
-		body, _ := ioutil.ReadAll(getResp.Body)
-		if len(body) > 0 {
-			return nil, util.ActionableError{Message: string(body)}
-		}
+	if getResp.StatusCode >= 400 {
 		return nil, errors.Errorf("unexpected result from get request: %d", getResp.StatusCode)
 	}
 
-	updateSequence := getResp.Header.Get("X-Replicated-ChannelSequence")
-	updateChannel := getResp.Header.Get("X-Replicated-ChannelName")
+	updateCursor := getResp.Header.Get("X-Replicated-ChannelSequence")
 	versionLabel := getResp.Header.Get("X-Replicated-VersionLabel")
 
 	gzf, err := gzip.NewReader(getResp.Body)
@@ -357,11 +341,8 @@ func downloadReplicatedApp(replicatedUpstream *ReplicatedUpstream, license *kots
 	}
 
 	release := Release{
-		Manifests: make(map[string][]byte),
-		UpdateCursor: ReplicatedCursor{
-			ChannelName: updateChannel,
-			Cursor:      updateSequence,
-		},
+		Manifests:    make(map[string][]byte),
+		UpdateCursor: updateCursor,
 		VersionLabel: versionLabel,
 		// NOTE: release notes come from Application spec
 	}
@@ -397,7 +378,7 @@ func downloadReplicatedApp(replicatedUpstream *ReplicatedUpstream, license *kots
 	return &release, nil
 }
 
-func listPendingChannelReleases(replicatedUpstream *ReplicatedUpstream, license *kotsv1beta1.License, cursor ReplicatedCursor) ([]ChannelRelease, error) {
+func listPendingChannelReleases(replicatedUpstream *ReplicatedUpstream, license *kotsv1beta1.License, channelSequence string) ([]ChannelRelease, error) {
 	u, err := url.Parse(license.Spec.Endpoint)
 	if err != nil {
 		return nil, errors.Wrap(err, "failed to parse endpoint from license")
@@ -408,15 +389,7 @@ func listPendingChannelReleases(replicatedUpstream *ReplicatedUpstream, license
 		hostname = fmt.Sprintf("%s:%s", u.Hostname(), u.Port())
 	}
 
-	sequence := cursor.Cursor
-	if license.Spec.ChannelName != cursor.ChannelName {
-		sequence = ""
-	}
-
-	urlValues := url.Values{}
-	urlValues.Set("channelSequence", sequence)
-	urlValues.Add("licenseSequence", fmt.Sprintf("%d", license.Spec.LicenseSequence))
-	url := fmt.Sprintf("%s://%s/release/%s/pending?%s", u.Scheme, hostname, license.Spec.AppSlug, urlValues.Encode())
+	url := fmt.Sprintf("%s://%s/release/%s/pending?channelSequence=%s", u.Scheme, hostname, license.Spec.AppSlug, channelSequence)
 
 	req, err := http.NewRequest("GET", url, nil)
 	if err != nil {
@@ -432,18 +405,15 @@ func listPendingChannelReleases(replicatedUpstream *ReplicatedUpstream, license
 	}
 	defer resp.Body.Close()
 
+	if resp.StatusCode >= 400 {
+		return nil, errors.Errorf("unexpected result from get request: %d", resp.StatusCode)
+	}
+
 	body, err := ioutil.ReadAll(resp.Body)
 	if err != nil {
 		return nil, errors.Wrap(err, "failed to read response body")
 	}
 
-	if resp.StatusCode >= 400 {
-		if len(body) > 0 {
-			return nil, util.ActionableError{Message: string(body)}
-		}
-		return nil, errors.Errorf("unexpected result from get request: %d", resp.StatusCode)
-	}
-
 	var channelReleases struct {
 		ChannelReleases []ChannelRelease `json:"channelReleases"`
 	}
diff --git a/pkg/upstream/types/types.go b/pkg/upstream/types/types.go
index e06ef2892..0ded5ce8f 100644
--- a/pkg/upstream/types/types.go
+++ b/pkg/upstream/types/types.go
@@ -22,7 +22,6 @@ type Upstream struct {
 	Type          string
 	Files         []UpstreamFile
 	UpdateCursor  string
-	ChannelName   string
 	VersionLabel  string
 	ReleaseNotes  string
 	EncryptionKey string
diff --git a/pkg/upstream/write.go b/pkg/upstream/write.go
index 900c76deb..cd05399b3 100644
--- a/pkg/upstream/write.go
+++ b/pkg/upstream/write.go
@@ -115,7 +115,6 @@ func WriteUpstream(u *types.Upstream, options types.WriteOptions) error {
 		},
 		Spec: kotsv1beta1.InstallationSpec{
 			UpdateCursor:  u.UpdateCursor,
-			ChannelName:   u.ChannelName,
 			VersionLabel:  u.VersionLabel,
 			ReleaseNotes:  u.ReleaseNotes,
 			EncryptionKey: encryptionKey,
diff --git a/pkg/util/util.go b/pkg/util/util.go
index 123d6f7c0..c926365b9 100644
--- a/pkg/util/util.go
+++ b/pkg/util/util.go
@@ -2,7 +2,6 @@ package util
 
 import (
 	"bytes"
-	"fmt"
 	"net/url"
 )
 
@@ -61,11 +60,3 @@ func IntPointer(x int) *int64 {
 	xout = int64(x)
 	return &xout
 }
-
-type ActionableError struct {
-	Message string
-}
-
-func (e ActionableError) Error() string {
-	return fmt.Sprintf("%s", e.Message)
-}
diff --git a/support-bundle.yaml b/support-bundle.yaml
index 0d57c3c1d..58854035b 100644
--- a/support-bundle.yaml
+++ b/support-bundle.yaml
@@ -3,4 +3,33 @@ kind: Collector
 metadata:
   name: collector-sample
 spec:
-  collectors: []
+  collectors:
+    - clusterInfo: {}
+    - clusterResources: {}
+    - exec: 
+        args: 
+          - "-U"
+          - kotsadm
+        collectorName: kotsadm-postgres-db
+        command: 
+          - pg_dump
+        containerName: kotsadm-postgres
+        name: kots/admin_console
+        selector: 
+          - app=kotsadm-postgres
+        timeout: 10s
+    - logs: 
+        collectorName: kotsadm-api
+        name: kots/admin_console
+        selector: 
+          - app=kotsadm-api
+    - logs: 
+        collectorName: kotsadm-operator
+        name: kots/admin_console
+        selector: 
+          - app=kotsadm-operator
+    - secret: 
+        collectorName: kotsadm-replicated-registry
+        includeValue: false
+        key: .dockerconfigjson
+        name: kotsadm-replicated-registry


Test patch:
diff --git a/pkg/upstream/replicated_test.go b/pkg/upstream/replicated_test.go
index 7f5b432de..efa4a8785 100644
--- a/pkg/upstream/replicated_test.go
+++ b/pkg/upstream/replicated_test.go
@@ -325,13 +325,7 @@ func Test_getRequest(t *testing.T) {
 		r := &ReplicatedUpstream{
 			Channel: test.channel,
 		}
-		cursor := ReplicatedCursor{
-			Cursor: test.channelSequence,
-		}
-		if test.channel != nil {
-			cursor.ChannelName = *test.channel
-		}
-		request, err := r.getRequest("GET", license, cursor)
+		request, err := r.getRequest("GET", license, test.channelSequence)
 		req.NoError(err)
 		assert.Equal(t, test.expectedURL, request.URL.String())
 	}