package migrations_test

import (
	"context"
	"database/sql"
	"testing"

	"github.com/stretchr/testify/require"

	"github.com/operator-framework/operator-registry/pkg/sqlite/migrations"
)

func TestReplacesSkipsUp(t *testing.T) {
	db, migrator, cleanup := CreateTestDBAt(t, migrations.ReplacesSkipsMigrationKey-1)
	defer cleanup()

	_, err := db.Exec(`PRAGMA foreign_keys = 0`)
	require.NoError(t, err)

	tx, err := db.Begin()
	require.NoError(t, err)

	// add test bundles
	etcd061csv := `{"apiVersion":"operators.coreos.com/v1alpha1","kind":"ClusterServiceVersion","metadata":{"annotations":{"tectonic-visibility":"ocs"},"name":"etcdoperator.v0.6.1","namespace":"placeholder"},"spec":{"customresourcedefinitions":{"provided":[{"description":"Represents a cluster of etcd nodes.","displayName":"etcd Cluster","kind":"EtcdCluster","name":"etcdclusters.etcd.database.coreos.com","resources":[{"kind":"Service","version":"v1"},{"kind":"Pod","version":"v1"}],"specDescriptors":[{"description":"The desired number of member Pods for the etcd cluster.","displayName":"Size","path":"size","x-descriptors":["urn:alm:descriptor:com.tectonic.ui:podCount"]}],"statusDescriptors":[{"description":"The status of each of the member Pods for the etcd cluster.","displayName":"Member Status","path":"members","x-descriptors":["urn:alm:descriptor:com.tectonic.ui:podStatuses"]},{"description":"The service at which the running etcd cluster can be accessed.","displayName":"Service","path":"service","x-descriptors":["urn:alm:descriptor:io.kubernetes:Service"]},{"description":"The current size of the etcd cluster.","displayName":"Cluster Size","path":"size"},{"description":"The current version of the etcd cluster.","displayName":"Current version","path":"currentVersion"},{"description":"The target version of the etcd cluster, after upgrading.","displayName":"Target version","path":"targetVersion"},{"description":"The current status of the etcd cluster.","displayName":"Status","path":"phase","x-descriptors":["urn:alm:descriptor:io.kubernetes.phase"]},{"description":"Explanation for the current status of the cluster.","displayName":"Status Details","path":"reason","x-descriptors":["urn:alm:descriptor:io.kubernetes.phase:reason"]}],"version":"v1beta2"}]},"description":"etcd is a distributed key value store that provides a reliable way to store data across a cluster of machines. It’s open-source and available on GitHub. etcd gracefully handles leader elections during network partitions and will tolerate machine failure, including the leader. Your applications can read and write data into etcd.\nA simple use-case is to store database connection details or feature flags within etcd as key value pairs. These values can be watched, allowing your app to reconfigure itself when they change. Advanced uses take advantage of the consistency guarantees to implement database leader elections or do distributed locking across a cluster of workers.\n\n_The etcd Open Cloud Service is Public Alpha. The goal before Beta is to fully implement backup features._\n\n### Reading and writing to etcd\n\nCommunicate with etcd though its command line utility etcdctl or with the API using the Kubernetes Service.\n\n[Read the complete guide to using the etcd Open Cloud Service](https://coreos.com/tectonic/docs/latest/alm/etcd-ocs.html)\n\n### Supported Features\n**High availability**\nMultiple instances of etcd are networked together and secured. Individual failures or networking issues are transparently handled to keep your cluster up and running.\n**Automated updates**\nRolling out a new etcd version works like all Kubernetes rolling updates. Simply declare the desired version, and the etcd service starts a safe rolling update to the new version automatically.\n**Backups included**\nComing soon, the ability to schedule backups to happen on or off cluster.\n","displayName":"etcd","icon":[{"base64data":" ","mediatype":"image/png"}],"install":{"spec":{"deployments":[{"name":"etcd-operator","spec":{"replicas":1,"selector":{"matchLabels":{"name":"etcd-operator-alm-owned"}},"template":{"metadata":{"labels":{"name":"etcd-operator-alm-owned"},"name":"etcd-operator-alm-owned"},"spec":{"containers":[{"command":["etcd-operator","--create-crd=false"],"env":[{"name":"MY_POD_NAMESPACE","valueFrom":{"fieldRef":{"fieldPath":"metadata.namespace"}}},{"name":"MY_POD_NAME","valueFrom":{"fieldRef":{"fieldPath":"metadata.name"}}}],"image":"quay.io/coreos/etcd-operator@sha256:bd944a211eaf8f31da5e6d69e8541e7cada8f16a9f7a5a570b22478997819943","name":"etcd-operator"}],"serviceAccountName":"etcd-operator"}}}}],"permissions":[{"rules":[{"apiGroups":["etcd.database.coreos.com"],"resources":["etcdclusters"],"verbs":["*"]},{"apiGroups":["storage.k8s.io"],"resources":["storageclasses"],"verbs":["*"]},{"apiGroups":[""],"resources":["pods","services","endpoints","persistentvolumeclaims","events"],"verbs":["*"]},{"apiGroups":["apps"],"resources":["deployments"],"verbs":["*"]},{"apiGroups":[""],"resources":["secrets"],"verbs":["get"]}],"serviceAccountName":"etcd-operator"}]},"strategy":"deployment"},"keywords":["etcd","key value","database","coreos","open source"],"labels":{"alm-owner-etcd":"etcdoperator","alm-status-descriptors":"etcdoperator.v0.6.1","operated-by":"etcdoperator"},"links":[{"name":"Blog","url":"https://coreos.com/etcd"},{"name":"Documentation","url":"https://coreos.com/operators/etcd/docs/latest/"},{"name":"etcd Operator Source Code","url":"https://github.com/coreos/etcd-operator"}],"maintainers":[{"email":"support@coreos.com","name":"CoreOS, Inc"}],"maturity":"alpha","provider":{"name":"CoreOS, Inc"},"selector":{"matchLabels":{"alm-owner-etcd":"etcdoperator","operated-by":"etcdoperator"}},"version":"0.6.1"}}`
	etcd061bundle := `{"apiVersion":"apiextensions.k8s.io/v1beta1","kind":"CustomResourceDefinition","metadata":{"name":"etcdclusters.etcd.database.coreos.com"},"spec":{"group":"etcd.database.coreos.com","names":{"kind":"EtcdCluster","listKind":"EtcdClusterList","plural":"etcdclusters","shortNames":["etcdclus","etcd"],"singular":"etcdcluster"},"scope":"Namespaced","version":"v1beta2"}}{"apiVersion":"operators.coreos.com/v1alpha1","kind":"ClusterServiceVersion","metadata":{"annotations":{"tectonic-visibility":"ocs"},"name":"etcdoperator.v0.6.1","namespace":"placeholder"},"spec":{"customresourcedefinitions":{"required":[{"description":"Represents a cluster of etcd nodes.","displayName":"etcd Cluster","kind":"EtcdCluster","name":"etcdclusters.etcd.database.coreos.com","resources":[{"kind":"Service","version":"v1"},{"kind":"Pod","version":"v1"}],"specDescriptors":[{"description":"The desired number of member Pods for the etcd cluster.","displayName":"Size","path":"size","x-descriptors":["urn:alm:descriptor:com.tectonic.ui:podCount"]}],"statusDescriptors":[{"description":"The status of each of the member Pods for the etcd cluster.","displayName":"Member Status","path":"members","x-descriptors":["urn:alm:descriptor:com.tectonic.ui:podStatuses"]},{"description":"The service at which the running etcd cluster can be accessed.","displayName":"Service","path":"service","x-descriptors":["urn:alm:descriptor:io.kubernetes:Service"]},{"description":"The current size of the etcd cluster.","displayName":"Cluster Size","path":"size"},{"description":"The current version of the etcd cluster.","displayName":"Current version","path":"currentVersion"},{"description":"The target version of the etcd cluster, after upgrading.","displayName":"Target version","path":"targetVersion"},{"description":"The current status of the etcd cluster.","displayName":"Status","path":"phase","x-descriptors":["urn:alm:descriptor:io.kubernetes.phase"]},{"description":"Explanation for the current status of the cluster.","displayName":"Status Details","path":"reason","x-descriptors":["urn:alm:descriptor:io.kubernetes.phase:reason"]}],"version":"v1beta2"}]},"description":"etcd is a distributed key value store that provides a reliable way to store data across a cluster of machines. It’s open-source and available on GitHub. etcd gracefully handles leader elections during network partitions and will tolerate machine failure, including the leader. Your applications can read and write data into etcd.\nA simple use-case is to store database connection details or feature flags within etcd as key value pairs. These values can be watched, allowing your app to reconfigure itself when they change. Advanced uses take advantage of the consistency guarantees to implement database leader elections or do distributed locking across a cluster of workers.\n\n_The etcd Open Cloud Service is Public Alpha. The goal before Beta is to fully implement backup features._\n\n### Reading and writing to etcd\n\nCommunicate with etcd though its command line utility etcdctl or with the API using the Kubernetes Service.\n\n[Read the complete guide to using the etcd Open Cloud Service](https://coreos.com/tectonic/docs/latest/alm/etcd-ocs.html)\n\n### Supported Features\n**High availability**\nMultiple instances of etcd are networked together and secured. Individual failures or networking issues are transparently handled to keep your cluster up and running.\n**Automated updates**\nRolling out a new etcd version works like all Kubernetes rolling updates. Simply declare the desired version, and the etcd service starts a safe rolling update to the new version automatically.\n**Backups included**\nComing soon, the ability to schedule backups to happen on or off cluster.\n","displayName":"etcd","icon":[{"base64data":" ","mediatype":"image/png"}],"install":{"spec":{"deployments":[{"name":"etcd-operator","spec":{"replicas":1,"selector":{"matchLabels":{"name":"etcd-operator-alm-owned"}},"template":{"metadata":{"labels":{"name":"etcd-operator-alm-owned"},"name":"etcd-operator-alm-owned"},"spec":{"containers":[{"command":["etcd-operator","--create-crd=false"],"env":[{"name":"MY_POD_NAMESPACE","valueFrom":{"fieldRef":{"fieldPath":"metadata.namespace"}}},{"name":"MY_POD_NAME","valueFrom":{"fieldRef":{"fieldPath":"metadata.name"}}}],"image":"quay.io/coreos/etcd-operator@sha256:bd944a211eaf8f31da5e6d69e8541e7cada8f16a9f7a5a570b22478997819943","name":"etcd-operator"}],"serviceAccountName":"etcd-operator"}}}}],"permissions":[{"rules":[{"apiGroups":["etcd.database.coreos.com"],"resources":["etcdclusters"],"verbs":["*"]},{"apiGroups":["storage.k8s.io"],"resources":["storageclasses"],"verbs":["*"]},{"apiGroups":[""],"resources":["pods","services","endpoints","persistentvolumeclaims","events"],"verbs":["*"]},{"apiGroups":["apps"],"resources":["deployments"],"verbs":["*"]},{"apiGroups":[""],"resources":["secrets"],"verbs":["get"]}],"serviceAccountName":"etcd-operator"}]},"strategy":"deployment"},"keywords":["etcd","key value","database","coreos","open source"],"labels":{"alm-owner-etcd":"etcdoperator","alm-status-descriptors":"etcdoperator.v0.6.1","operated-by":"etcdoperator"},"links":[{"name":"Blog","url":"https://coreos.com/etcd"},{"name":"Documentation","url":"https://coreos.com/operators/etcd/docs/latest/"},{"name":"etcd Operator Source Code","url":"https://github.com/coreos/etcd-operator"}],"maintainers":[{"email":"support@coreos.com","name":"CoreOS, Inc"}],"maturity":"alpha","provider":{"name":"CoreOS, Inc"},"selector":{"matchLabels":{"alm-owner-etcd":"etcdoperator","operated-by":"etcdoperator"}},"version":"0.6.1"}}`
	etcd090csv := `{"apiVersion":"operators.coreos.com/v1alpha1","kind":"ClusterServiceVersion","metadata":{"annotations":{"alm-examples":"[{\"apiVersion\":\"etcd.database.coreos.com/v1beta2\",\"kind\":\"EtcdCluster\",\"metadata\":{\"name\":\"example\",\"namespace\":\"default\"},\"spec\":{\"size\":3,\"version\":\"3.2.13\"}},{\"apiVersion\":\"etcd.database.coreos.com/v1beta2\",\"kind\":\"EtcdRestore\",\"metadata\":{\"name\":\"example-etcd-cluster\"},\"spec\":{\"etcdCluster\":{\"name\":\"example-etcd-cluster\"},\"backupStorageType\":\"S3\",\"s3\":{\"path\":\"\u003cfull-s3-path\u003e\",\"awsSecret\":\"\u003caws-secret\u003e\"}}},{\"apiVersion\":\"etcd.database.coreos.com/v1beta2\",\"kind\":\"EtcdBackup\",\"metadata\":{\"name\":\"example-etcd-cluster-backup\"},\"spec\":{\"etcdEndpoints\":[\"\u003cetcd-cluster-endpoints\u003e\"],\"storageType\":\"S3\",\"s3\":{\"path\":\"\u003cfull-s3-path\u003e\",\"awsSecret\":\"\u003caws-secret\u003e\"}}}]","tectonic-visibility":"ocs"},"name":"etcdoperator.v0.9.0","namespace":"placeholder"},"spec":{"customresourcedefinitions":{"owned":[{"description":"Represents a cluster of etcd nodes.","displayName":"etcd Cluster","kind":"EtcdCluster","name":"etcdclusters.etcd.database.coreos.com","resources":[{"kind":"Service","version":"v1"},{"kind":"Pod","version":"v1"}],"specDescriptors":[{"description":"The desired number of member Pods for the etcd cluster.","displayName":"Size","path":"size","x-descriptors":["urn:alm:descriptor:com.tectonic.ui:podCount"]},{"description":"Limits describes the minimum/maximum amount of compute resources required/allowed","displayName":"Resource Requirements","path":"pod.resources","x-descriptors":["urn:alm:descriptor:com.tectonic.ui:resourceRequirements"]}],"statusDescriptors":[{"description":"The status of each of the member Pods for the etcd cluster.","displayName":"Member Status","path":"members","x-descriptors":["urn:alm:descriptor:com.tectonic.ui:podStatuses"]},{"description":"The service at which the running etcd cluster can be accessed.","displayName":"Service","path":"serviceName","x-descriptors":["urn:alm:descriptor:io.kubernetes:Service"]},{"description":"The current size of the etcd cluster.","displayName":"Cluster Size","path":"size"},{"description":"The current version of the etcd cluster.","displayName":"Current Version","path":"currentVersion"},{"description":"The target version of the etcd cluster, after upgrading.","displayName":"Target Version","path":"targetVersion"},{"description":"The current status of the etcd cluster.","displayName":"Status","path":"phase","x-descriptors":["urn:alm:descriptor:io.kubernetes.phase"]},{"description":"Explanation for the current status of the cluster.","displayName":"Status Details","path":"reason","x-descriptors":["urn:alm:descriptor:io.kubernetes.phase:reason"]}],"version":"v1beta2"},{"description":"Represents the intent to backup an etcd cluster.","displayName":"etcd Backup","kind":"EtcdBackup","name":"etcdbackups.etcd.database.coreos.com","specDescriptors":[{"description":"Specifies the endpoints of an etcd cluster.","displayName":"etcd Endpoint(s)","path":"etcdEndpoints","x-descriptors":["urn:alm:descriptor:etcd:endpoint"]},{"description":"The full AWS S3 path where the backup is saved.","displayName":"S3 Path","path":"s3.path","x-descriptors":["urn:alm:descriptor:aws:s3:path"]},{"description":"The name of the secret object that stores the AWS credential and config files.","displayName":"AWS Secret","path":"s3.awsSecret","x-descriptors":["urn:alm:descriptor:io.kubernetes:Secret"]}],"statusDescriptors":[{"description":"Indicates if the backup was successful.","displayName":"Succeeded","path":"succeeded","x-descriptors":["urn:alm:descriptor:text"]},{"description":"Indicates the reason for any backup related failures.","displayName":"Reason","path":"reason","x-descriptors":["urn:alm:descriptor:io.kubernetes.phase:reason"]}],"version":"v1beta2"},{"description":"Represents the intent to restore an etcd cluster from a backup.","displayName":"etcd Restore","kind":"EtcdRestore","name":"etcdrestores.etcd.database.coreos.com","specDescriptors":[{"description":"References the EtcdCluster which should be restored,","displayName":"etcd Cluster","path":"etcdCluster.name","x-descriptors":["urn:alm:descriptor:io.kubernetes:EtcdCluster","urn:alm:descriptor:text"]},{"description":"The full AWS S3 path where the backup is saved.","displayName":"S3 Path","path":"s3.path","x-descriptors":["urn:alm:descriptor:aws:s3:path"]},{"description":"The name of the secret object that stores the AWS credential and config files.","displayName":"AWS Secret","path":"s3.awsSecret","x-descriptors":["urn:alm:descriptor:io.kubernetes:Secret"]}],"statusDescriptors":[{"description":"Indicates if the restore was successful.","displayName":"Succeeded","path":"succeeded","x-descriptors":["urn:alm:descriptor:text"]},{"description":"Indicates the reason for any restore related failures.","displayName":"Reason","path":"reason","x-descriptors":["urn:alm:descriptor:io.kubernetes.phase:reason"]}],"version":"v1beta2"}]},"description":"etcd is a distributed key value store that provides a reliable way to store data across a cluster of machines. It’s open-source and available on GitHub. etcd gracefully handles leader elections during network partitions and will tolerate machine failure, including the leader. Your applications can read and write data into etcd.\nA simple use-case is to store database connection details or feature flags within etcd as key value pairs. These values can be watched, allowing your app to reconfigure itself when they change. Advanced uses take advantage of the consistency guarantees to implement database leader elections or do distributed locking across a cluster of workers.\n\n_The etcd Open Cloud Service is Public Alpha. The goal before Beta is to fully implement backup features._\n\n### Reading and writing to etcd\n\nCommunicate with etcd though its command line utility etcdctl or with the API using the Kubernetes Service.\n\n[Read the complete guide to using the etcd Open Cloud Service](https://coreos.com/tectonic/docs/latest/alm/etcd-ocs.html)\n\n### Supported Features\n\n\n**High availability**\n\n\nMultiple instances of etcd are networked together and secured. Individual failures or networking issues are transparently handled to keep your cluster up and running.\n\n\n**Automated updates**\n\n\nRolling out a new etcd version works like all Kubernetes rolling updates. Simply declare the desired version, and the etcd service starts a safe rolling update to the new version automatically.\n\n\n**Backups included**\n\n\nComing soon, the ability to schedule backups to happen on or off cluster.\n","displayName":"etcd","icon":[{"base64data":" ","mediatype":"image/png"}],"install":{"spec":{"deployments":[{"name":"etcd-operator","spec":{"replicas":1,"selector":{"matchLabels":{"name":"etcd-operator-alm-owned"}},"template":{"metadata":{"labels":{"name":"etcd-operator-alm-owned"},"name":"etcd-operator-alm-owned"},"spec":{"containers":[{"command":["etcd-operator","--create-crd=false"],"env":[{"name":"MY_POD_NAMESPACE","valueFrom":{"fieldRef":{"fieldPath":"metadata.namespace"}}},{"name":"MY_POD_NAME","valueFrom":{"fieldRef":{"fieldPath":"metadata.name"}}}],"image":"quay.io/coreos/etcd-operator@sha256:db563baa8194fcfe39d1df744ed70024b0f1f9e9b55b5923c2f3a413c44dc6b8","name":"etcd-operator"},{"command":["etcd-backup-operator","--create-crd=false"],"env":[{"name":"MY_POD_NAMESPACE","valueFrom":{"fieldRef":{"fieldPath":"metadata.namespace"}}},{"name":"MY_POD_NAME","valueFrom":{"fieldRef":{"fieldPath":"metadata.name"}}}],"image":"quay.io/coreos/etcd-operator@sha256:db563baa8194fcfe39d1df744ed70024b0f1f9e9b55b5923c2f3a413c44dc6b8","name":"etcd-backup-operator"},{"command":["etcd-restore-operator","--create-crd=false"],"env":[{"name":"MY_POD_NAMESPACE","valueFrom":{"fieldRef":{"fieldPath":"metadata.namespace"}}},{"name":"MY_POD_NAME","valueFrom":{"fieldRef":{"fieldPath":"metadata.name"}}}],"image":"quay.io/coreos/etcd-operator@sha256:db563baa8194fcfe39d1df744ed70024b0f1f9e9b55b5923c2f3a413c44dc6b8","name":"etcd-restore-operator"}],"serviceAccountName":"etcd-operator"}}}}],"permissions":[{"rules":[{"apiGroups":["etcd.database.coreos.com"],"resources":["etcdclusters","etcdbackups","etcdrestores"],"verbs":["*"]},{"apiGroups":[""],"resources":["pods","services","endpoints","persistentvolumeclaims","events"],"verbs":["*"]},{"apiGroups":["apps"],"resources":["deployments"],"verbs":["*"]},{"apiGroups":[""],"resources":["secrets"],"verbs":["get"]}],"serviceAccountName":"etcd-operator"}]},"strategy":"deployment"},"keywords":["etcd","key value","database","coreos","open source"],"labels":{"alm-owner-etcd":"etcdoperator","operated-by":"etcdoperator"},"links":[{"name":"Blog","url":"https://coreos.com/etcd"},{"name":"Documentation","url":"https://coreos.com/operators/etcd/docs/latest/"},{"name":"etcd Operator Source Code","url":"https://github.com/coreos/etcd-operator"}],"maintainers":[{"email":"support@coreos.com","name":"CoreOS, Inc"}],"maturity":"alpha","provider":{"name":"CoreOS, Inc"},"replaces":"etcdoperator.v0.6.1","selector":{"matchLabels":{"alm-owner-etcd":"etcdoperator","operated-by":"etcdoperator"}},"version":"0.9.0"}}`
	etcd090bundle := `{"apiVersion":"apiextensions.k8s.io/v1beta1","kind":"CustomResourceDefinition","metadata":{"name":"etcdbackups.etcd.database.coreos.com"},"spec":{"group":"etcd.database.coreos.com","names":{"kind":"EtcdBackup","listKind":"EtcdBackupList","plural":"etcdbackups","singular":"etcdbackup"},"scope":"Namespaced","version":"v1beta2"}}{"apiVersion":"apiextensions.k8s.io/v1beta1","kind":"CustomResourceDefinition","metadata":{"name":"etcdclusters.etcd.database.coreos.com"},"spec":{"group":"etcd.database.coreos.com","names":{"kind":"EtcdCluster","listKind":"EtcdClusterList","plural":"etcdclusters","shortNames":["etcdclus","etcd"],"singular":"etcdcluster"},"scope":"Namespaced","version":"v1beta2"}}{"apiVersion":"operators.coreos.com/v1alpha1","kind":"ClusterServiceVersion","metadata":{"annotations":{"alm-examples":"[{\"apiVersion\":\"etcd.database.coreos.com/v1beta2\",\"kind\":\"EtcdCluster\",\"metadata\":{\"name\":\"example\",\"namespace\":\"default\"},\"spec\":{\"size\":3,\"version\":\"3.2.13\"}},{\"apiVersion\":\"etcd.database.coreos.com/v1beta2\",\"kind\":\"EtcdRestore\",\"metadata\":{\"name\":\"example-etcd-cluster\"},\"spec\":{\"etcdCluster\":{\"name\":\"example-etcd-cluster\"},\"backupStorageType\":\"S3\",\"s3\":{\"path\":\"\u003cfull-s3-path\u003e\",\"awsSecret\":\"\u003caws-secret\u003e\"}}},{\"apiVersion\":\"etcd.database.coreos.com/v1beta2\",\"kind\":\"EtcdBackup\",\"metadata\":{\"name\":\"example-etcd-cluster-backup\"},\"spec\":{\"etcdEndpoints\":[\"\u003cetcd-cluster-endpoints\u003e\"],\"storageType\":\"S3\",\"s3\":{\"path\":\"\u003cfull-s3-path\u003e\",\"awsSecret\":\"\u003caws-secret\u003e\"}}}]","tectonic-visibility":"ocs"},"name":"etcdoperator.v0.9.0","namespace":"placeholder"},"spec":{"customresourcedefinitions":{"owned":[{"description":"Represents a cluster of etcd nodes.","displayName":"etcd Cluster","kind":"EtcdCluster","name":"etcdclusters.etcd.database.coreos.com","resources":[{"kind":"Service","version":"v1"},{"kind":"Pod","version":"v1"}],"specDescriptors":[{"description":"The desired number of member Pods for the etcd cluster.","displayName":"Size","path":"size","x-descriptors":["urn:alm:descriptor:com.tectonic.ui:podCount"]},{"description":"Limits describes the minimum/maximum amount of compute resources required/allowed","displayName":"Resource Requirements","path":"pod.resources","x-descriptors":["urn:alm:descriptor:com.tectonic.ui:resourceRequirements"]}],"statusDescriptors":[{"description":"The status of each of the member Pods for the etcd cluster.","displayName":"Member Status","path":"members","x-descriptors":["urn:alm:descriptor:com.tectonic.ui:podStatuses"]},{"description":"The service at which the running etcd cluster can be accessed.","displayName":"Service","path":"serviceName","x-descriptors":["urn:alm:descriptor:io.kubernetes:Service"]},{"description":"The current size of the etcd cluster.","displayName":"Cluster Size","path":"size"},{"description":"The current version of the etcd cluster.","displayName":"Current Version","path":"currentVersion"},{"description":"The target version of the etcd cluster, after upgrading.","displayName":"Target Version","path":"targetVersion"},{"description":"The current status of the etcd cluster.","displayName":"Status","path":"phase","x-descriptors":["urn:alm:descriptor:io.kubernetes.phase"]},{"description":"Explanation for the current status of the cluster.","displayName":"Status Details","path":"reason","x-descriptors":["urn:alm:descriptor:io.kubernetes.phase:reason"]}],"version":"v1beta2"},{"description":"Represents the intent to backup an etcd cluster.","displayName":"etcd Backup","kind":"EtcdBackup","name":"etcdbackups.etcd.database.coreos.com","specDescriptors":[{"description":"Specifies the endpoints of an etcd cluster.","displayName":"etcd Endpoint(s)","path":"etcdEndpoints","x-descriptors":["urn:alm:descriptor:etcd:endpoint"]},{"description":"The full AWS S3 path where the backup is saved.","displayName":"S3 Path","path":"s3.path","x-descriptors":["urn:alm:descriptor:aws:s3:path"]},{"description":"The name of the secret object that stores the AWS credential and config files.","displayName":"AWS Secret","path":"s3.awsSecret","x-descriptors":["urn:alm:descriptor:io.kubernetes:Secret"]}],"statusDescriptors":[{"description":"Indicates if the backup was successful.","displayName":"Succeeded","path":"succeeded","x-descriptors":["urn:alm:descriptor:text"]},{"description":"Indicates the reason for any backup related failures.","displayName":"Reason","path":"reason","x-descriptors":["urn:alm:descriptor:io.kubernetes.phase:reason"]}],"version":"v1beta2"},{"description":"Represents the intent to restore an etcd cluster from a backup.","displayName":"etcd Restore","kind":"EtcdRestore","name":"etcdrestores.etcd.database.coreos.com","specDescriptors":[{"description":"References the EtcdCluster which should be restored,","displayName":"etcd Cluster","path":"etcdCluster.name","x-descriptors":["urn:alm:descriptor:io.kubernetes:EtcdCluster","urn:alm:descriptor:text"]},{"description":"The full AWS S3 path where the backup is saved.","displayName":"S3 Path","path":"s3.path","x-descriptors":["urn:alm:descriptor:aws:s3:path"]},{"description":"The name of the secret object that stores the AWS credential and config files.","displayName":"AWS Secret","path":"s3.awsSecret","x-descriptors":["urn:alm:descriptor:io.kubernetes:Secret"]}],"statusDescriptors":[{"description":"Indicates if the restore was successful.","displayName":"Succeeded","path":"succeeded","x-descriptors":["urn:alm:descriptor:text"]},{"description":"Indicates the reason for any restore related failures.","displayName":"Reason","path":"reason","x-descriptors":["urn:alm:descriptor:io.kubernetes.phase:reason"]}],"version":"v1beta2"}]},"description":"etcd is a distributed key value store that provides a reliable way to store data across a cluster of machines. It’s open-source and available on GitHub. etcd gracefully handles leader elections during network partitions and will tolerate machine failure, including the leader. Your applications can read and write data into etcd.\nA simple use-case is to store database connection details or feature flags within etcd as key value pairs. These values can be watched, allowing your app to reconfigure itself when they change. Advanced uses take advantage of the consistency guarantees to implement database leader elections or do distributed locking across a cluster of workers.\n\n_The etcd Open Cloud Service is Public Alpha. The goal before Beta is to fully implement backup features._\n\n### Reading and writing to etcd\n\nCommunicate with etcd though its command line utility etcdctl or with the API using the Kubernetes Service.\n\n[Read the complete guide to using the etcd Open Cloud Service](https://coreos.com/tectonic/docs/latest/alm/etcd-ocs.html)\n\n### Supported Features\n\n\n**High availability**\n\n\nMultiple instances of etcd are networked together and secured. Individual failures or networking issues are transparently handled to keep your cluster up and running.\n\n\n**Automated updates**\n\n\nRolling out a new etcd version works like all Kubernetes rolling updates. Simply declare the desired version, and the etcd service starts a safe rolling update to the new version automatically.\n\n\n**Backups included**\n\n\nComing soon, the ability to schedule backups to happen on or off cluster.\n","displayName":"etcd","icon":[{"base64data":" ","mediatype":"image/png"}],"install":{"spec":{"deployments":[{"name":"etcd-operator","spec":{"replicas":1,"selector":{"matchLabels":{"name":"etcd-operator-alm-owned"}},"template":{"metadata":{"labels":{"name":"etcd-operator-alm-owned"},"name":"etcd-operator-alm-owned"},"spec":{"containers":[{"command":["etcd-operator","--create-crd=false"],"env":[{"name":"MY_POD_NAMESPACE","valueFrom":{"fieldRef":{"fieldPath":"metadata.namespace"}}},{"name":"MY_POD_NAME","valueFrom":{"fieldRef":{"fieldPath":"metadata.name"}}}],"image":"quay.io/coreos/etcd-operator@sha256:db563baa8194fcfe39d1df744ed70024b0f1f9e9b55b5923c2f3a413c44dc6b8","name":"etcd-operator"},{"command":["etcd-backup-operator","--create-crd=false"],"env":[{"name":"MY_POD_NAMESPACE","valueFrom":{"fieldRef":{"fieldPath":"metadata.namespace"}}},{"name":"MY_POD_NAME","valueFrom":{"fieldRef":{"fieldPath":"metadata.name"}}}],"image":"quay.io/coreos/etcd-operator@sha256:db563baa8194fcfe39d1df744ed70024b0f1f9e9b55b5923c2f3a413c44dc6b8","name":"etcd-backup-operator"},{"command":["etcd-restore-operator","--create-crd=false"],"env":[{"name":"MY_POD_NAMESPACE","valueFrom":{"fieldRef":{"fieldPath":"metadata.namespace"}}},{"name":"MY_POD_NAME","valueFrom":{"fieldRef":{"fieldPath":"metadata.name"}}}],"image":"quay.io/coreos/etcd-operator@sha256:db563baa8194fcfe39d1df744ed70024b0f1f9e9b55b5923c2f3a413c44dc6b8","name":"etcd-restore-operator"}],"serviceAccountName":"etcd-operator"}}}}],"permissions":[{"rules":[{"apiGroups":["etcd.database.coreos.com"],"resources":["etcdclusters","etcdbackups","etcdrestores"],"verbs":["*"]},{"apiGroups":[""],"resources":["pods","services","endpoints","persistentvolumeclaims","events"],"verbs":["*"]},{"apiGroups":["apps"],"resources":["deployments"],"verbs":["*"]},{"apiGroups":[""],"resources":["secrets"],"verbs":["get"]}],"serviceAccountName":"etcd-operator"}]},"strategy":"deployment"},"keywords":["etcd","key value","database","coreos","open source"],"labels":{"alm-owner-etcd":"etcdoperator","operated-by":"etcdoperator"},"links":[{"name":"Blog","url":"https://coreos.com/etcd"},{"name":"Documentation","url":"https://coreos.com/operators/etcd/docs/latest/"},{"name":"etcd Operator Source Code","url":"https://github.com/coreos/etcd-operator"}],"maintainers":[{"email":"support@coreos.com","name":"CoreOS, Inc"}],"maturity":"alpha","provider":{"name":"CoreOS, Inc"},"replaces":"etcdoperator.v0.6.1","selector":{"matchLabels":{"alm-owner-etcd":"etcdoperator","operated-by":"etcdoperator"}},"version":"0.9.0"}}{"apiVersion":"apiextensions.k8s.io/v1beta1","kind":"CustomResourceDefinition","metadata":{"name":"etcdrestores.etcd.database.coreos.com"},"spec":{"group":"etcd.database.coreos.com","names":{"kind":"EtcdRestore","listKind":"EtcdRestoreList","plural":"etcdrestores","singular":"etcdrestore"},"scope":"Namespaced","version":"v1beta2"}}`
	etcd092csv := `{"apiVersion":"operators.coreos.com/v1alpha1","kind":"ClusterServiceVersion","metadata":{"annotations":{"alm-examples":"[{\"apiVersion\":\"etcd.database.coreos.com/v1beta2\",\"kind\":\"EtcdCluster\",\"metadata\":{\"name\":\"example\",\"namespace\":\"default\"},\"spec\":{\"size\":3,\"version\":\"3.2.13\"}},{\"apiVersion\":\"etcd.database.coreos.com/v1beta2\",\"kind\":\"EtcdRestore\",\"metadata\":{\"name\":\"example-etcd-cluster\"},\"spec\":{\"etcdCluster\":{\"name\":\"example-etcd-cluster\"},\"backupStorageType\":\"S3\",\"s3\":{\"path\":\"\u003cfull-s3-path\u003e\",\"awsSecret\":\"\u003caws-secret\u003e\"}}},{\"apiVersion\":\"etcd.database.coreos.com/v1beta2\",\"kind\":\"EtcdBackup\",\"metadata\":{\"name\":\"example-etcd-cluster-backup\"},\"spec\":{\"etcdEndpoints\":[\"\u003cetcd-cluster-endpoints\u003e\"],\"storageType\":\"S3\",\"s3\":{\"path\":\"\u003cfull-s3-path\u003e\",\"awsSecret\":\"\u003caws-secret\u003e\"}}}]","olm.skipRange":"\u003c 0.6.0","tectonic-visibility":"ocs"},"name":"etcdoperator.v0.9.2","namespace":"placeholder"},"spec":{"customresourcedefinitions":{"owned":[{"description":"Represents a cluster of etcd nodes.","displayName":"etcd Cluster","kind":"EtcdCluster","name":"etcdclusters.etcd.database.coreos.com","resources":[{"kind":"Service","version":"v1"},{"kind":"Pod","version":"v1"}],"specDescriptors":[{"description":"The desired number of member Pods for the etcd cluster.","displayName":"Size","path":"size","x-descriptors":["urn:alm:descriptor:com.tectonic.ui:podCount"]},{"description":"Limits describes the minimum/maximum amount of compute resources required/allowed","displayName":"Resource Requirements","path":"pod.resources","x-descriptors":["urn:alm:descriptor:com.tectonic.ui:resourceRequirements"]}],"statusDescriptors":[{"description":"The status of each of the member Pods for the etcd cluster.","displayName":"Member Status","path":"members","x-descriptors":["urn:alm:descriptor:com.tectonic.ui:podStatuses"]},{"description":"The service at which the running etcd cluster can be accessed.","displayName":"Service","path":"serviceName","x-descriptors":["urn:alm:descriptor:io.kubernetes:Service"]},{"description":"The current size of the etcd cluster.","displayName":"Cluster Size","path":"size"},{"description":"The current version of the etcd cluster.","displayName":"Current Version","path":"currentVersion"},{"description":"The target version of the etcd cluster, after upgrading.","displayName":"Target Version","path":"targetVersion"},{"description":"The current status of the etcd cluster.","displayName":"Status","path":"phase","x-descriptors":["urn:alm:descriptor:io.kubernetes.phase"]},{"description":"Explanation for the current status of the cluster.","displayName":"Status Details","path":"reason","x-descriptors":["urn:alm:descriptor:io.kubernetes.phase:reason"]}],"version":"v1beta2"},{"description":"Represents the intent to backup an etcd cluster.","displayName":"etcd Backup","kind":"EtcdBackup","name":"etcdbackups.etcd.database.coreos.com","specDescriptors":[{"description":"Specifies the endpoints of an etcd cluster.","displayName":"etcd Endpoint(s)","path":"etcdEndpoints","x-descriptors":["urn:alm:descriptor:etcd:endpoint"]},{"description":"The full AWS S3 path where the backup is saved.","displayName":"S3 Path","path":"s3.path","x-descriptors":["urn:alm:descriptor:aws:s3:path"]},{"description":"The name of the secret object that stores the AWS credential and config files.","displayName":"AWS Secret","path":"s3.awsSecret","x-descriptors":["urn:alm:descriptor:io.kubernetes:Secret"]}],"statusDescriptors":[{"description":"Indicates if the backup was successful.","displayName":"Succeeded","path":"succeeded","x-descriptors":["urn:alm:descriptor:text"]},{"description":"Indicates the reason for any backup related failures.","displayName":"Reason","path":"reason","x-descriptors":["urn:alm:descriptor:io.kubernetes.phase:reason"]}],"version":"v1beta2"},{"description":"Represents the intent to restore an etcd cluster from a backup.","displayName":"etcd Restore","kind":"EtcdRestore","name":"etcdrestores.etcd.database.coreos.com","specDescriptors":[{"description":"References the EtcdCluster which should be restored,","displayName":"etcd Cluster","path":"etcdCluster.name","x-descriptors":["urn:alm:descriptor:io.kubernetes:EtcdCluster","urn:alm:descriptor:text"]},{"description":"The full AWS S3 path where the backup is saved.","displayName":"S3 Path","path":"s3.path","x-descriptors":["urn:alm:descriptor:aws:s3:path"]},{"description":"The name of the secret object that stores the AWS credential and config files.","displayName":"AWS Secret","path":"s3.awsSecret","x-descriptors":["urn:alm:descriptor:io.kubernetes:Secret"]}],"statusDescriptors":[{"description":"Indicates if the restore was successful.","displayName":"Succeeded","path":"succeeded","x-descriptors":["urn:alm:descriptor:text"]},{"description":"Indicates the reason for any restore related failures.","displayName":"Reason","path":"reason","x-descriptors":["urn:alm:descriptor:io.kubernetes.phase:reason"]}],"version":"v1beta2"}],"required":[{"description":"Represents a cluster of etcd nodes.","displayName":"etcd Cluster","kind":"EtcdCluster","name":"etcdclusters.etcd.database.coreos.com","resources":[{"kind":"Service","version":"v1"},{"kind":"Pod","version":"v1"}],"specDescriptors":[{"description":"The desired number of member Pods for the etcd cluster.","displayName":"Size","path":"size","x-descriptors":["urn:alm:descriptor:com.tectonic.ui:podCount"]}],"version":"v1beta2"}]},"description":"etcd is a distributed key value store that provides a reliable way to store data across a cluster of machines. It’s open-source and available on GitHub. etcd gracefully handles leader elections during network partitions and will tolerate machine failure, including the leader. Your applications can read and write data into etcd.\nA simple use-case is to store database connection details or feature flags within etcd as key value pairs. These values can be watched, allowing your app to reconfigure itself when they change. Advanced uses take advantage of the consistency guarantees to implement database leader elections or do distributed locking across a cluster of workers.\n\n_The etcd Open Cloud Service is Public Alpha. The goal before Beta is to fully implement backup features._\n\n### Reading and writing to etcd\n\nCommunicate with etcd though its command line utility etcdctl or with the API using the Kubernetes Service.\n\n[Read the complete guide to using the etcd Open Cloud Service](https://coreos.com/tectonic/docs/latest/alm/etcd-ocs.html)\n\n### Supported Features\n\n\n**High availability**\n\n\nMultiple instances of etcd are networked together and secured. Individual failures or networking issues are transparently handled to keep your cluster up and running.\n\n\n**Automated updates**\n\n\nRolling out a new etcd version works like all Kubernetes rolling updates. Simply declare the desired version, and the etcd service starts a safe rolling update to the new version automatically.\n\n\n**Backups included**\n\n\nComing soon, the ability to schedule backups to happen on or off cluster.\n","displayName":"etcd","icon":[{"base64data":" ","mediatype":"image/png"}],"install":{"spec":{"deployments":[{"name":"etcd-operator","spec":{"replicas":1,"selector":{"matchLabels":{"name":"etcd-operator-alm-owned"}},"template":{"metadata":{"labels":{"name":"etcd-operator-alm-owned"},"name":"etcd-operator-alm-owned"},"spec":{"containers":[{"command":["etcd-operator","--create-crd=false"],"env":[{"name":"MY_POD_NAMESPACE","valueFrom":{"fieldRef":{"fieldPath":"metadata.namespace"}}},{"name":"MY_POD_NAME","valueFrom":{"fieldRef":{"fieldPath":"metadata.name"}}}],"image":"quay.io/coreos/etcd-operator@sha256:c0301e4686c3ed4206e370b42de5a3bd2229b9fb4906cf85f3f30650424abec2","name":"etcd-operator"},{"command":["etcd-backup-operator","--create-crd=false"],"env":[{"name":"MY_POD_NAMESPACE","valueFrom":{"fieldRef":{"fieldPath":"metadata.namespace"}}},{"name":"MY_POD_NAME","valueFrom":{"fieldRef":{"fieldPath":"metadata.name"}}}],"image":"quay.io/coreos/etcd-operator@sha256:c0301e4686c3ed4206e370b42de5a3bd2229b9fb4906cf85f3f30650424abec2","name":"etcd-backup-operator"},{"command":["etcd-restore-operator","--create-crd=false"],"env":[{"name":"MY_POD_NAMESPACE","valueFrom":{"fieldRef":{"fieldPath":"metadata.namespace"}}},{"name":"MY_POD_NAME","valueFrom":{"fieldRef":{"fieldPath":"metadata.name"}}}],"image":"quay.io/coreos/etcd-operator@sha256:c0301e4686c3ed4206e370b42de5a3bd2229b9fb4906cf85f3f30650424abec2","name":"etcd-restore-operator"}],"serviceAccountName":"etcd-operator"}}}}],"permissions":[{"rules":[{"apiGroups":["etcd.database.coreos.com"],"resources":["etcdclusters","etcdbackups","etcdrestores"],"verbs":["*"]},{"apiGroups":[""],"resources":["pods","services","endpoints","persistentvolumeclaims","events"],"verbs":["*"]},{"apiGroups":["apps"],"resources":["deployments"],"verbs":["*"]},{"apiGroups":[""],"resources":["secrets"],"verbs":["get"]}],"serviceAccountName":"etcd-operator"}]},"strategy":"deployment"},"keywords":["etcd","key value","database","coreos","open source"],"labels":{"alm-owner-etcd":"etcdoperator","operated-by":"etcdoperator"},"links":[{"name":"Blog","url":"https://coreos.com/etcd"},{"name":"Documentation","url":"https://coreos.com/operators/etcd/docs/latest/"},{"name":"etcd Operator Source Code","url":"https://github.com/coreos/etcd-operator"}],"maintainers":[{"email":"support@coreos.com","name":"CoreOS, Inc"}],"maturity":"alpha","provider":{"name":"CoreOS, Inc"},"relatedImages":[{"image":"quay.io/coreos/etcd@sha256:3816b6daf9b66d6ced6f0f966314e2d4f894982c6b1493061502f8c2bf86ac84","name":"etcd-v3.4.0"},{"image":"quay.io/coreos/etcd@sha256:49d3d4a81e0d030d3f689e7167f23e120abf955f7d08dbedf3ea246485acee9f","name":"etcd-3.4.1"}],"replaces":"etcdoperator.v0.9.0","selector":{"matchLabels":{"alm-owner-etcd":"etcdoperator","operated-by":"etcdoperator"}},"skips":["etcdoperator.v0.9.1"],"version":"0.9.2"}}`
	etcd092bundle := `{"apiVersion":"apiextensions.k8s.io/v1beta1","kind":"CustomResourceDefinition","metadata":{"name":"etcdbackups.etcd.database.coreos.com"},"spec":{"group":"etcd.database.coreos.com","names":{"kind":"EtcdBackup","listKind":"EtcdBackupList","plural":"etcdbackups","singular":"etcdbackup"},"scope":"Namespaced","version":"v1beta2"}}{"apiVersion":"apiextensions.k8s.io/v1beta1","kind":"CustomResourceDefinition","metadata":{"name":"etcdclusters.etcd.database.coreos.com"},"spec":{"group":"etcd.database.coreos.com","names":{"kind":"EtcdCluster","listKind":"EtcdClusterList","plural":"etcdclusters","shortNames":["etcdclus","etcd"],"singular":"etcdcluster"},"scope":"Namespaced","version":"v1beta2"}}{"apiVersion":"operators.coreos.com/v1alpha1","kind":"ClusterServiceVersion","metadata":{"annotations":{"alm-examples":"[{\"apiVersion\":\"etcd.database.coreos.com/v1beta2\",\"kind\":\"EtcdCluster\",\"metadata\":{\"name\":\"example\",\"namespace\":\"default\"},\"spec\":{\"size\":3,\"version\":\"3.2.13\"}},{\"apiVersion\":\"etcd.database.coreos.com/v1beta2\",\"kind\":\"EtcdRestore\",\"metadata\":{\"name\":\"example-etcd-cluster\"},\"spec\":{\"etcdCluster\":{\"name\":\"example-etcd-cluster\"},\"backupStorageType\":\"S3\",\"s3\":{\"path\":\"\u003cfull-s3-path\u003e\",\"awsSecret\":\"\u003caws-secret\u003e\"}}},{\"apiVersion\":\"etcd.database.coreos.com/v1beta2\",\"kind\":\"EtcdBackup\",\"metadata\":{\"name\":\"example-etcd-cluster-backup\"},\"spec\":{\"etcdEndpoints\":[\"\u003cetcd-cluster-endpoints\u003e\"],\"storageType\":\"S3\",\"s3\":{\"path\":\"\u003cfull-s3-path\u003e\",\"awsSecret\":\"\u003caws-secret\u003e\"}}}]","olm.skipRange":"\u003c 0.6.0","tectonic-visibility":"ocs"},"name":"etcdoperator.v0.9.2","namespace":"placeholder"},"spec":{"customresourcedefinitions":{"owned":[{"description":"Represents a cluster of etcd nodes.","displayName":"etcd Cluster","kind":"EtcdCluster","name":"etcdclusters.etcd.database.coreos.com","resources":[{"kind":"Service","version":"v1"},{"kind":"Pod","version":"v1"}],"specDescriptors":[{"description":"The desired number of member Pods for the etcd cluster.","displayName":"Size","path":"size","x-descriptors":["urn:alm:descriptor:com.tectonic.ui:podCount"]},{"description":"Limits describes the minimum/maximum amount of compute resources required/allowed","displayName":"Resource Requirements","path":"pod.resources","x-descriptors":["urn:alm:descriptor:com.tectonic.ui:resourceRequirements"]}],"statusDescriptors":[{"description":"The status of each of the member Pods for the etcd cluster.","displayName":"Member Status","path":"members","x-descriptors":["urn:alm:descriptor:com.tectonic.ui:podStatuses"]},{"description":"The service at which the running etcd cluster can be accessed.","displayName":"Service","path":"serviceName","x-descriptors":["urn:alm:descriptor:io.kubernetes:Service"]},{"description":"The current size of the etcd cluster.","displayName":"Cluster Size","path":"size"},{"description":"The current version of the etcd cluster.","displayName":"Current Version","path":"currentVersion"},{"description":"The target version of the etcd cluster, after upgrading.","displayName":"Target Version","path":"targetVersion"},{"description":"The current status of the etcd cluster.","displayName":"Status","path":"phase","x-descriptors":["urn:alm:descriptor:io.kubernetes.phase"]},{"description":"Explanation for the current status of the cluster.","displayName":"Status Details","path":"reason","x-descriptors":["urn:alm:descriptor:io.kubernetes.phase:reason"]}],"version":"v1beta2"},{"description":"Represents the intent to backup an etcd cluster.","displayName":"etcd Backup","kind":"EtcdBackup","name":"etcdbackups.etcd.database.coreos.com","specDescriptors":[{"description":"Specifies the endpoints of an etcd cluster.","displayName":"etcd Endpoint(s)","path":"etcdEndpoints","x-descriptors":["urn:alm:descriptor:etcd:endpoint"]},{"description":"The full AWS S3 path where the backup is saved.","displayName":"S3 Path","path":"s3.path","x-descriptors":["urn:alm:descriptor:aws:s3:path"]},{"description":"The name of the secret object that stores the AWS credential and config files.","displayName":"AWS Secret","path":"s3.awsSecret","x-descriptors":["urn:alm:descriptor:io.kubernetes:Secret"]}],"statusDescriptors":[{"description":"Indicates if the backup was successful.","displayName":"Succeeded","path":"succeeded","x-descriptors":["urn:alm:descriptor:text"]},{"description":"Indicates the reason for any backup related failures.","displayName":"Reason","path":"reason","x-descriptors":["urn:alm:descriptor:io.kubernetes.phase:reason"]}],"version":"v1beta2"},{"description":"Represents the intent to restore an etcd cluster from a backup.","displayName":"etcd Restore","kind":"EtcdRestore","name":"etcdrestores.etcd.database.coreos.com","specDescriptors":[{"description":"References the EtcdCluster which should be restored,","displayName":"etcd Cluster","path":"etcdCluster.name","x-descriptors":["urn:alm:descriptor:io.kubernetes:EtcdCluster","urn:alm:descriptor:text"]},{"description":"The full AWS S3 path where the backup is saved.","displayName":"S3 Path","path":"s3.path","x-descriptors":["urn:alm:descriptor:aws:s3:path"]},{"description":"The name of the secret object that stores the AWS credential and config files.","displayName":"AWS Secret","path":"s3.awsSecret","x-descriptors":["urn:alm:descriptor:io.kubernetes:Secret"]}],"statusDescriptors":[{"description":"Indicates if the restore was successful.","displayName":"Succeeded","path":"succeeded","x-descriptors":["urn:alm:descriptor:text"]},{"description":"Indicates the reason for any restore related failures.","displayName":"Reason","path":"reason","x-descriptors":["urn:alm:descriptor:io.kubernetes.phase:reason"]}],"version":"v1beta2"}],"required":[{"description":"Represents a cluster of etcd nodes.","displayName":"etcd Cluster","kind":"EtcdCluster","name":"etcdclusters.etcd.database.coreos.com","resources":[{"kind":"Service","version":"v1"},{"kind":"Pod","version":"v1"}],"specDescriptors":[{"description":"The desired number of member Pods for the etcd cluster.","displayName":"Size","path":"size","x-descriptors":["urn:alm:descriptor:com.tectonic.ui:podCount"]}],"version":"v1beta2"}]},"description":"etcd is a distributed key value store that provides a reliable way to store data across a cluster of machines. It’s open-source and available on GitHub. etcd gracefully handles leader elections during network partitions and will tolerate machine failure, including the leader. Your applications can read and write data into etcd.\nA simple use-case is to store database connection details or feature flags within etcd as key value pairs. These values can be watched, allowing your app to reconfigure itself when they change. Advanced uses take advantage of the consistency guarantees to implement database leader elections or do distributed locking across a cluster of workers.\n\n_The etcd Open Cloud Service is Public Alpha. The goal before Beta is to fully implement backup features._\n\n### Reading and writing to etcd\n\nCommunicate with etcd though its command line utility etcdctl or with the API using the Kubernetes Service.\n\n[Read the complete guide to using the etcd Open Cloud Service](https://coreos.com/tectonic/docs/latest/alm/etcd-ocs.html)\n\n### Supported Features\n\n\n**High availability**\n\n\nMultiple instances of etcd are networked together and secured. Individual failures or networking issues are transparently handled to keep your cluster up and running.\n\n\n**Automated updates**\n\n\nRolling out a new etcd version works like all Kubernetes rolling updates. Simply declare the desired version, and the etcd service starts a safe rolling update to the new version automatically.\n\n\n**Backups included**\n\n\nComing soon, the ability to schedule backups to happen on or off cluster.\n","displayName":"etcd","icon":[{"base64data":" ","mediatype":"image/png"}],"install":{"spec":{"deployments":[{"name":"etcd-operator","spec":{"replicas":1,"selector":{"matchLabels":{"name":"etcd-operator-alm-owned"}},"template":{"metadata":{"labels":{"name":"etcd-operator-alm-owned"},"name":"etcd-operator-alm-owned"},"spec":{"containers":[{"command":["etcd-operator","--create-crd=false"],"env":[{"name":"MY_POD_NAMESPACE","valueFrom":{"fieldRef":{"fieldPath":"metadata.namespace"}}},{"name":"MY_POD_NAME","valueFrom":{"fieldRef":{"fieldPath":"metadata.name"}}}],"image":"quay.io/coreos/etcd-operator@sha256:c0301e4686c3ed4206e370b42de5a3bd2229b9fb4906cf85f3f30650424abec2","name":"etcd-operator"},{"command":["etcd-backup-operator","--create-crd=false"],"env":[{"name":"MY_POD_NAMESPACE","valueFrom":{"fieldRef":{"fieldPath":"metadata.namespace"}}},{"name":"MY_POD_NAME","valueFrom":{"fieldRef":{"fieldPath":"metadata.name"}}}],"image":"quay.io/coreos/etcd-operator@sha256:c0301e4686c3ed4206e370b42de5a3bd2229b9fb4906cf85f3f30650424abec2","name":"etcd-backup-operator"},{"command":["etcd-restore-operator","--create-crd=false"],"env":[{"name":"MY_POD_NAMESPACE","valueFrom":{"fieldRef":{"fieldPath":"metadata.namespace"}}},{"name":"MY_POD_NAME","valueFrom":{"fieldRef":{"fieldPath":"metadata.name"}}}],"image":"quay.io/coreos/etcd-operator@sha256:c0301e4686c3ed4206e370b42de5a3bd2229b9fb4906cf85f3f30650424abec2","name":"etcd-restore-operator"}],"serviceAccountName":"etcd-operator"}}}}],"permissions":[{"rules":[{"apiGroups":["etcd.database.coreos.com"],"resources":["etcdclusters","etcdbackups","etcdrestores"],"verbs":["*"]},{"apiGroups":[""],"resources":["pods","services","endpoints","persistentvolumeclaims","events"],"verbs":["*"]},{"apiGroups":["apps"],"resources":["deployments"],"verbs":["*"]},{"apiGroups":[""],"resources":["secrets"],"verbs":["get"]}],"serviceAccountName":"etcd-operator"}]},"strategy":"deployment"},"keywords":["etcd","key value","database","coreos","open source"],"labels":{"alm-owner-etcd":"etcdoperator","operated-by":"etcdoperator"},"links":[{"name":"Blog","url":"https://coreos.com/etcd"},{"name":"Documentation","url":"https://coreos.com/operators/etcd/docs/latest/"},{"name":"etcd Operator Source Code","url":"https://github.com/coreos/etcd-operator"}],"maintainers":[{"email":"support@coreos.com","name":"CoreOS, Inc"}],"maturity":"alpha","provider":{"name":"CoreOS, Inc"},"relatedImages":[{"image":"quay.io/coreos/etcd@sha256:3816b6daf9b66d6ced6f0f966314e2d4f894982c6b1493061502f8c2bf86ac84","name":"etcd-v3.4.0"},{"image":"quay.io/coreos/etcd@sha256:49d3d4a81e0d030d3f689e7167f23e120abf955f7d08dbedf3ea246485acee9f","name":"etcd-3.4.1"}],"replaces":"etcdoperator.v0.9.0","selector":{"matchLabels":{"alm-owner-etcd":"etcdoperator","operated-by":"etcdoperator"}},"skips":["etcdoperator.v0.9.1"],"version":"0.9.2"}}{"apiVersion":"apiextensions.k8s.io/v1beta1","kind":"CustomResourceDefinition","metadata":{"name":"etcdrestores.etcd.database.coreos.com"},"spec":{"group":"etcd.database.coreos.com","names":{"kind":"EtcdRestore","listKind":"EtcdRestoreList","plural":"etcdrestores","singular":"etcdrestore"},"scope":"Namespaced","version":"v1beta2"}}`
	insertBundle := "insert into operatorbundle(name, csv, bundle) values(?, ?, ?)"

	_, err = tx.Exec(insertBundle, "etcdoperator.v0.6.1", etcd061csv, etcd061bundle)
	require.NoError(t, err)
	_, err = tx.Exec(insertBundle, "etcdoperator.v0.9.0", etcd090csv, etcd090bundle)
	require.NoError(t, err)
	_, err = tx.Exec(insertBundle, "etcdoperator.v0.9.2", etcd092csv, etcd092bundle)
	require.NoError(t, err)

	_, err = tx.Exec("insert into package(name, default_channel) values(?,?)", "etcd", "alpha")
	require.NoError(t, err)
	_, err = tx.Exec("insert into channel(name, package_name, head_operatorbundle_name) values(?,?,?)", "alpha", "etcd", "etcdoperator.v0.9.2")
	require.NoError(t, err)
	_, err = tx.Exec("insert into channel(name, package_name, head_operatorbundle_name) values(?,?,?)", "beta", "etcd", "etcdoperator.v0.9.0")
	require.NoError(t, err)
	_, err = tx.Exec("insert into channel(name, package_name, head_operatorbundle_name) values(?,?,?)", "stable", "etcd", "etcdoperator.v0.9.2")
	require.NoError(t, err)

	channelEntries := `
	INSERT INTO "main"."channel_entry" ("entry_id", "channel_name", "package_name", "operatorbundle_name", "replaces", "depth") VALUES ('1', 'alpha', 'etcd', 'etcdoperator.v0.9.2', '4', '0');
	INSERT INTO "main"."channel_entry" ("entry_id", "channel_name", "package_name", "operatorbundle_name", "replaces", "depth") VALUES ('2', 'alpha', 'etcd', 'etcdoperator.v0.9.1', '', '1');
	INSERT INTO "main"."channel_entry" ("entry_id", "channel_name", "package_name", "operatorbundle_name", "replaces", "depth") VALUES ('3', 'alpha', 'etcd', 'etcdoperator.v0.9.2', '2', '1');
	INSERT INTO "main"."channel_entry" ("entry_id", "channel_name", "package_name", "operatorbundle_name", "replaces", "depth") VALUES ('4', 'alpha', 'etcd', 'etcdoperator.v0.9.0', '5', '2');
	INSERT INTO "main"."channel_entry" ("entry_id", "channel_name", "package_name", "operatorbundle_name", "replaces", "depth") VALUES ('5', 'alpha', 'etcd', 'etcdoperator.v0.6.1', '', '3');
	INSERT INTO "main"."channel_entry" ("entry_id", "channel_name", "package_name", "operatorbundle_name", "replaces", "depth") VALUES ('6', 'alpha', 'etcd', 'etcdoperator.v0.5.0', '', '4');
	INSERT INTO "main"."channel_entry" ("entry_id", "channel_name", "package_name", "operatorbundle_name", "replaces", "depth") VALUES ('7', 'alpha', 'etcd', 'etcdoperator.v0.6.1', '6', '4');
	INSERT INTO "main"."channel_entry" ("entry_id", "channel_name", "package_name", "operatorbundle_name", "replaces", "depth") VALUES ('8', 'alpha', 'etcd', 'etcdoperator.v0.4.1', '', '5');
	INSERT INTO "main"."channel_entry" ("entry_id", "channel_name", "package_name", "operatorbundle_name", "replaces", "depth") VALUES ('9', 'alpha', 'etcd', 'etcdoperator.v0.6.1', '8', '5');
	INSERT INTO "main"."channel_entry" ("entry_id", "channel_name", "package_name", "operatorbundle_name", "replaces", "depth") VALUES ('10', 'alpha', 'etcd', 'etcdoperator.v0.3.2-a', '', '6');
	INSERT INTO "main"."channel_entry" ("entry_id", "channel_name", "package_name", "operatorbundle_name", "replaces", "depth") VALUES ('11', 'alpha', 'etcd', 'etcdoperator.v0.6.1', '10', '6');
	INSERT INTO "main"."channel_entry" ("entry_id", "channel_name", "package_name", "operatorbundle_name", "replaces", "depth") VALUES ('12', 'beta', 'etcd', 'etcdoperator.v0.9.0', '13', '0');
	INSERT INTO "main"."channel_entry" ("entry_id", "channel_name", "package_name", "operatorbundle_name", "replaces", "depth") VALUES ('13', 'beta', 'etcd', 'etcdoperator.v0.6.1', '', '1');
	INSERT INTO "main"."channel_entry" ("entry_id", "channel_name", "package_name", "operatorbundle_name", "replaces", "depth") VALUES ('14', 'beta', 'etcd', 'etcdoperator.v0.5.0', '', '2');
	INSERT INTO "main"."channel_entry" ("entry_id", "channel_name", "package_name", "operatorbundle_name", "replaces", "depth") VALUES ('15', 'beta', 'etcd', 'etcdoperator.v0.6.1', '14', '2');
	INSERT INTO "main"."channel_entry" ("entry_id", "channel_name", "package_name", "operatorbundle_name", "replaces", "depth") VALUES ('16', 'beta', 'etcd', 'etcdoperator.v0.4.1', '', '3');
	INSERT INTO "main"."channel_entry" ("entry_id", "channel_name", "package_name", "operatorbundle_name", "replaces", "depth") VALUES ('17', 'beta', 'etcd', 'etcdoperator.v0.6.1', '16', '3');
	INSERT INTO "main"."channel_entry" ("entry_id", "channel_name", "package_name", "operatorbundle_name", "replaces", "depth") VALUES ('18', 'beta', 'etcd', 'etcdoperator.v0.3.2-a', '', '4');
	INSERT INTO "main"."channel_entry" ("entry_id", "channel_name", "package_name", "operatorbundle_name", "replaces", "depth") VALUES ('19', 'beta', 'etcd', 'etcdoperator.v0.6.1', '18', '4');
	INSERT INTO "main"."channel_entry" ("entry_id", "channel_name", "package_name", "operatorbundle_name", "replaces", "depth") VALUES ('20', 'stable', 'etcd', 'etcdoperator.v0.9.2', '23', '0');
	INSERT INTO "main"."channel_entry" ("entry_id", "channel_name", "package_name", "operatorbundle_name", "replaces", "depth") VALUES ('21', 'stable', 'etcd', 'etcdoperator.v0.9.1', '', '1');
	INSERT INTO "main"."channel_entry" ("entry_id", "channel_name", "package_name", "operatorbundle_name", "replaces", "depth") VALUES ('22', 'stable', 'etcd', 'etcdoperator.v0.9.2', '21', '1');
	INSERT INTO "main"."channel_entry" ("entry_id", "channel_name", "package_name", "operatorbundle_name", "replaces", "depth") VALUES ('23', 'stable', 'etcd', 'etcdoperator.v0.9.0', '24', '2');
	INSERT INTO "main"."channel_entry" ("entry_id", "channel_name", "package_name", "operatorbundle_name", "replaces", "depth") VALUES ('24', 'stable', 'etcd', 'etcdoperator.v0.6.1', '', '3');
	INSERT INTO "main"."channel_entry" ("entry_id", "channel_name", "package_name", "operatorbundle_name", "replaces", "depth") VALUES ('25', 'stable', 'etcd', 'etcdoperator.v0.5.0', '', '4');
	INSERT INTO "main"."channel_entry" ("entry_id", "channel_name", "package_name", "operatorbundle_name", "replaces", "depth") VALUES ('26', 'stable', 'etcd', 'etcdoperator.v0.6.1', '25', '4');
	INSERT INTO "main"."channel_entry" ("entry_id", "channel_name", "package_name", "operatorbundle_name", "replaces", "depth") VALUES ('27', 'stable', 'etcd', 'etcdoperator.v0.4.1', '', '5');
	INSERT INTO "main"."channel_entry" ("entry_id", "channel_name", "package_name", "operatorbundle_name", "replaces", "depth") VALUES ('28', 'stable', 'etcd', 'etcdoperator.v0.6.1', '27', '5');
	INSERT INTO "main"."channel_entry" ("entry_id", "channel_name", "package_name", "operatorbundle_name", "replaces", "depth") VALUES ('29', 'stable', 'etcd', 'etcdoperator.v0.3.2-a', '', '6');
	INSERT INTO "main"."channel_entry" ("entry_id", "channel_name", "package_name", "operatorbundle_name", "replaces", "depth") VALUES ('30', 'stable', 'etcd', 'etcdoperator.v0.6.1', '29', '6');	`
	_, err = tx.Exec(channelEntries)
	require.NoError(t, err)
	require.NoError(t, tx.Commit())

	_, err = db.Exec(`PRAGMA foreign_keys = 1`)
	require.NoError(t, err)

	err = migrator.Up(context.TODO(), migrations.Only(migrations.ReplacesSkipsMigrationKey))
	require.NoError(t, err)

	// Adding row with replaces and skips column should not fail after migrating up
	tx, err = db.Begin()
	require.NoError(t, err)
	stmt, err := tx.Prepare("insert into operatorbundle(name, csv, bundle, bundlepath, version, skiprange, replaces, skips) values(?, ?, ?, ?, ?, ?, ?, ?)")
	require.NoError(t, err)
	defer stmt.Close()
	_, err = stmt.Exec("testName", "newtestcsv", "newtestbundle", "quay.io/test", "1.0.0", "< 1.0.0", "0.9.0", "0.9.1,0.9.2")
	require.NoError(t, err)
	require.NoError(t, tx.Commit())

	getBundle := `SELECT replaces, skips FROM operatorbundle WHERE name=?`

	type test struct {
		name     string
		replaces string
		skips    string
	}
	tests := []test{
		{
			name:     "etcdoperator.v0.9.2",
			replaces: "etcdoperator.v0.9.0",
			skips:    "etcdoperator.v0.9.1",
		},
		{
			name:     "etcdoperator.v0.9.0",
			replaces: "etcdoperator.v0.6.1",
			skips:    "",
		},
		{
			name:     "etcdoperator.v0.6.1",
			replaces: "",
			skips:    "etcdoperator.v0.5.0,etcdoperator.v0.4.1,etcdoperator.v0.3.2-a",
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			rows, err := db.QueryContext(context.TODO(), getBundle, tt.name)
			require.NoError(t, err)
			require.True(t, rows.Next())
			var replacesSQL sql.NullString
			var skipsSQL sql.NullString
			require.NoError(t, rows.Scan(&replacesSQL, &skipsSQL))
			require.False(t, rows.Next())
			require.NoError(t, rows.Close())
			require.Equal(t, tt.replaces, replacesSQL.String)
			require.Equal(t, tt.skips, skipsSQL.String)
		})
	}
}

func TestReplacesSkipsDown(t *testing.T) {
	db, migrator, cleanup := CreateTestDBAt(t, migrations.ReplacesSkipsMigrationKey)
	defer cleanup()

	// Add a bundle
	testCSV := `{"apiVersion":"operators.coreos.com/v1alpha1","kind":"ClusterServiceVersion","metadata":{"annotations":{"tectonic-visibility":"ocs"},"name":"etcdoperator.v0.6.1","namespace":"placeholder"},"spec":{"customresourcedefinitions":{"required":[{"description":"Represents a cluster of etcd nodes.","displayName":"etcd Cluster","kind":"EtcdCluster","name":"etcdclusters.etcd.database.coreos.com","resources":[{"kind":"Service","version":"v1"},{"kind":"Pod","version":"v1"}],"specDescriptors":[{"description":"The desired number of member Pods for the etcd cluster.","displayName":"Size","path":"size","x-descriptors":["urn:alm:descriptor:com.tectonic.ui:podCount"]}],"statusDescriptors":[{"description":"The status of each of the member Pods for the etcd cluster.","displayName":"Member Status","path":"members","x-descriptors":["urn:alm:descriptor:com.tectonic.ui:podStatuses"]},{"description":"The service at which the running etcd cluster can be accessed.","displayName":"Service","path":"service","x-descriptors":["urn:alm:descriptor:io.kubernetes:Service"]},{"description":"The current size of the etcd cluster.","displayName":"Cluster Size","path":"size"},{"description":"The current version of the etcd cluster.","displayName":"Current version","path":"currentVersion"},{"description":"The target version of the etcd cluster, after upgrading.","displayName":"Target version","path":"targetVersion"},{"description":"The current status of the etcd cluster.","displayName":"Status","path":"phase","x-descriptors":["urn:alm:descriptor:io.kubernetes.phase"]},{"description":"Explanation for the current status of the cluster.","displayName":"Status Details","path":"reason","x-descriptors":["urn:alm:descriptor:io.kubernetes.phase:reason"]}],"version":"v1beta2"}]},"description":"etcd is a distributed key value store that provides a reliable way to store data across a cluster of machines. It’s open-source and available on GitHub. etcd gracefully handles leader elections during network partitions and will tolerate machine failure, including the leader. Your applications can read and write data into etcd.\nA simple use-case is to store database connection details or feature flags within etcd as key value pairs. These values can be watched, allowing your app to reconfigure itself when they change. Advanced uses take advantage of the consistency guarantees to implement database leader elections or do distributed locking across a cluster of workers.\n\n_The etcd Open Cloud Service is Public Alpha. The goal before Beta is to fully implement backup features._\n\n### Reading and writing to etcd\n\nCommunicate with etcd though its command line utility etcdctl or with the API using the Kubernetes Service.\n\n[Read the complete guide to using the etcd Open Cloud Service](https://coreos.com/tectonic/docs/latest/alm/etcd-ocs.html)\n\n### Supported Features\n**High availability**\nMultiple instances of etcd are networked together and secured. Individual failures or networking issues are transparently handled to keep your cluster up and running.\n**Automated updates**\nRolling out a new etcd version works like all Kubernetes rolling updates. Simply declare the desired version, and the etcd service starts a safe rolling update to the new version automatically.\n**Backups included**\nComing soon, the ability to schedule backups to happen on or off cluster.\n","displayName":"etcd","icon":[{"base64data":" ","mediatype":"image/png"}],"install":{"spec":{"deployments":[{"name":"etcd-operator","spec":{"replicas":1,"selector":{"matchLabels":{"name":"etcd-operator-alm-owned"}},"template":{"metadata":{"labels":{"name":"etcd-operator-alm-owned"},"name":"etcd-operator-alm-owned"},"spec":{"containers":[{"command":["etcd-operator","--create-crd=false"],"env":[{"name":"MY_POD_NAMESPACE","valueFrom":{"fieldRef":{"fieldPath":"metadata.namespace"}}},{"name":"MY_POD_NAME","valueFrom":{"fieldRef":{"fieldPath":"metadata.name"}}}],"image":"quay.io/coreos/etcd-operator@sha256:bd944a211eaf8f31da5e6d69e8541e7cada8f16a9f7a5a570b22478997819943","name":"etcd-operator"}],"serviceAccountName":"etcd-operator"}}}}],"permissions":[{"rules":[{"apiGroups":["etcd.database.coreos.com"],"resources":["etcdclusters"],"verbs":["*"]},{"apiGroups":["storage.k8s.io"],"resources":["storageclasses"],"verbs":["*"]},{"apiGroups":[""],"resources":["pods","services","endpoints","persistentvolumeclaims","events"],"verbs":["*"]},{"apiGroups":["apps"],"resources":["deployments"],"verbs":["*"]},{"apiGroups":[""],"resources":["secrets"],"verbs":["get"]}],"serviceAccountName":"etcd-operator"}]},"strategy":"deployment"},"keywords":["etcd","key value","database","coreos","open source"],"labels":{"alm-owner-etcd":"etcdoperator","alm-status-descriptors":"etcdoperator.v0.6.1","operated-by":"etcdoperator"},"links":[{"name":"Blog","url":"https://coreos.com/etcd"},{"name":"Documentation","url":"https://coreos.com/operators/etcd/docs/latest/"},{"name":"etcd Operator Source Code","url":"https://github.com/coreos/etcd-operator"}],"maintainers":[{"email":"support@coreos.com","name":"CoreOS, Inc"}],"maturity":"alpha","provider":{"name":"CoreOS, Inc"},"selector":{"matchLabels":{"alm-owner-etcd":"etcdoperator","operated-by":"etcdoperator"}},"version":"0.6.1"}}`
	testBundle := `{"apiVersion":"apiextensions.k8s.io/v1beta1","kind":"CustomResourceDefinition","metadata":{"name":"etcdclusters.etcd.database.coreos.com"},"spec":{"group":"etcd.database.coreos.com","names":{"kind":"EtcdCluster","listKind":"EtcdClusterList","plural":"etcdclusters","shortNames":["etcdclus","etcd"],"singular":"etcdcluster"},"scope":"Namespaced","version":"v1beta2"}}{"apiVersion":"operators.coreos.com/v1alpha1","kind":"ClusterServiceVersion","metadata":{"annotations":{"tectonic-visibility":"ocs"},"name":"etcdoperator.v0.6.1","namespace":"placeholder"},"spec":{"customresourcedefinitions":{"required":[{"description":"Represents a cluster of etcd nodes.","displayName":"etcd Cluster","kind":"EtcdCluster","name":"etcdclusters.etcd.database.coreos.com","resources":[{"kind":"Service","version":"v1"},{"kind":"Pod","version":"v1"}],"specDescriptors":[{"description":"The desired number of member Pods for the etcd cluster.","displayName":"Size","path":"size","x-descriptors":["urn:alm:descriptor:com.tectonic.ui:podCount"]}],"statusDescriptors":[{"description":"The status of each of the member Pods for the etcd cluster.","displayName":"Member Status","path":"members","x-descriptors":["urn:alm:descriptor:com.tectonic.ui:podStatuses"]},{"description":"The service at which the running etcd cluster can be accessed.","displayName":"Service","path":"service","x-descriptors":["urn:alm:descriptor:io.kubernetes:Service"]},{"description":"The current size of the etcd cluster.","displayName":"Cluster Size","path":"size"},{"description":"The current version of the etcd cluster.","displayName":"Current version","path":"currentVersion"},{"description":"The target version of the etcd cluster, after upgrading.","displayName":"Target version","path":"targetVersion"},{"description":"The current status of the etcd cluster.","displayName":"Status","path":"phase","x-descriptors":["urn:alm:descriptor:io.kubernetes.phase"]},{"description":"Explanation for the current status of the cluster.","displayName":"Status Details","path":"reason","x-descriptors":["urn:alm:descriptor:io.kubernetes.phase:reason"]}],"version":"v1beta2"}]},"description":"etcd is a distributed key value store that provides a reliable way to store data across a cluster of machines. It’s open-source and available on GitHub. etcd gracefully handles leader elections during network partitions and will tolerate machine failure, including the leader. Your applications can read and write data into etcd.\nA simple use-case is to store database connection details or feature flags within etcd as key value pairs. These values can be watched, allowing your app to reconfigure itself when they change. Advanced uses take advantage of the consistency guarantees to implement database leader elections or do distributed locking across a cluster of workers.\n\n_The etcd Open Cloud Service is Public Alpha. The goal before Beta is to fully implement backup features._\n\n### Reading and writing to etcd\n\nCommunicate with etcd though its command line utility etcdctl or with the API using the Kubernetes Service.\n\n[Read the complete guide to using the etcd Open Cloud Service](https://coreos.com/tectonic/docs/latest/alm/etcd-ocs.html)\n\n### Supported Features\n**High availability**\nMultiple instances of etcd are networked together and secured. Individual failures or networking issues are transparently handled to keep your cluster up and running.\n**Automated updates**\nRolling out a new etcd version works like all Kubernetes rolling updates. Simply declare the desired version, and the etcd service starts a safe rolling update to the new version automatically.\n**Backups included**\nComing soon, the ability to schedule backups to happen on or off cluster.\n","displayName":"etcd","icon":[{"base64data":" ","mediatype":"image/png"}],"install":{"spec":{"deployments":[{"name":"etcd-operator","spec":{"replicas":1,"selector":{"matchLabels":{"name":"etcd-operator-alm-owned"}},"template":{"metadata":{"labels":{"name":"etcd-operator-alm-owned"},"name":"etcd-operator-alm-owned"},"spec":{"containers":[{"command":["etcd-operator","--create-crd=false"],"env":[{"name":"MY_POD_NAMESPACE","valueFrom":{"fieldRef":{"fieldPath":"metadata.namespace"}}},{"name":"MY_POD_NAME","valueFrom":{"fieldRef":{"fieldPath":"metadata.name"}}}],"image":"quay.io/coreos/etcd-operator@sha256:bd944a211eaf8f31da5e6d69e8541e7cada8f16a9f7a5a570b22478997819943","name":"etcd-operator"}],"serviceAccountName":"etcd-operator"}}}}],"permissions":[{"rules":[{"apiGroups":["etcd.database.coreos.com"],"resources":["etcdclusters"],"verbs":["*"]},{"apiGroups":["storage.k8s.io"],"resources":["storageclasses"],"verbs":["*"]},{"apiGroups":[""],"resources":["pods","services","endpoints","persistentvolumeclaims","events"],"verbs":["*"]},{"apiGroups":["apps"],"resources":["deployments"],"verbs":["*"]},{"apiGroups":[""],"resources":["secrets"],"verbs":["get"]}],"serviceAccountName":"etcd-operator"}]},"strategy":"deployment"},"keywords":["etcd","key value","database","coreos","open source"],"labels":{"alm-owner-etcd":"etcdoperator","alm-status-descriptors":"etcdoperator.v0.6.1","operated-by":"etcdoperator"},"links":[{"name":"Blog","url":"https://coreos.com/etcd"},{"name":"Documentation","url":"https://coreos.com/operators/etcd/docs/latest/"},{"name":"etcd Operator Source Code","url":"https://github.com/coreos/etcd-operator"}],"maintainers":[{"email":"support@coreos.com","name":"CoreOS, Inc"}],"maturity":"alpha","provider":{"name":"CoreOS, Inc"},"selector":{"matchLabels":{"alm-owner-etcd":"etcdoperator","operated-by":"etcdoperator"}},"version":"0.6.1"}}`
	insert := "insert into operatorbundle(name, csv, bundle, bundlepath, version, skiprange, replaces, skips) values(?, ?, ?, ?, ?, ?, ?, ?)"
	_, err := db.Exec(insert, "etcdoperator.v0.6.1", testCSV, testBundle, "quay.io/image", "0.6.1", ">0.5.0 <0.6.1", "0.9.0", "0.9.1,0.9.2")
	require.NoError(t, err)

	// run down migration
	err = migrator.Down(context.TODO(), migrations.Only(migrations.ReplacesSkipsMigrationKey))
	require.NoError(t, err)

	// check that bundle data is fine
	var name sql.NullString
	var csv sql.NullString
	var bundle sql.NullString
	rows, err := db.Query("select name, csv, bundle from operatorbundle")
	require.NoError(t, err)
	rows.Next()
	require.NoError(t, rows.Scan(&name, &csv, &bundle))
	require.Equal(t, "etcdoperator.v0.6.1", name.String)
	require.Equal(t, csv.String, testCSV)
	require.Equal(t, bundle.String, testBundle)
	require.NoError(t, rows.Close())
}
