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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions defaults/defaults.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ const (
// ImageRegistryName is the name of the image-registry workload resource (deployment)
ImageRegistryName = "image-registry"

// PVCImageRegistryName is the default name of the claim provisioned for PVC backend
PVCImageRegistryName = "image-registry-storage"

// ImageRegistryResourceName is the name of the image registry config instance
ImageRegistryResourceName = "cluster"

Expand Down
77 changes: 76 additions & 1 deletion pkg/operator/bootstrap.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,18 @@ import (
"github.com/openshift/cluster-image-registry-operator/defaults"
"github.com/openshift/cluster-image-registry-operator/pkg/parameters"
"github.com/openshift/cluster-image-registry-operator/pkg/storage"
"github.com/openshift/cluster-image-registry-operator/pkg/storage/pvc"
"github.com/openshift/cluster-image-registry-operator/pkg/storage/swift"
"github.com/openshift/cluster-image-registry-operator/pkg/storage/util"

configapiv1 "github.com/openshift/api/config/v1"
imageregistryv1 "github.com/openshift/api/imageregistry/v1"
operatorapi "github.com/openshift/api/operator/v1"
regopset "github.com/openshift/client-go/imageregistry/clientset/versioned/typed/imageregistry/v1"
appsapi "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/klog"
)
Expand Down Expand Up @@ -61,6 +67,32 @@ func (c *Controller) Bootstrap() error {
mgmtState = operatorapi.Removed
}

infra, err := util.GetInfrastructure(c.listers)
if err != nil {
return err
}

rolloutStrategy := appsapi.RollingUpdateDeploymentStrategyType

// If Swift service is not available for OpenStack, we have to start using
// Cinder with RWO PVC backend. It means that we need to create an RWO claim
// and set the rollout strategy to Recreate.
switch infra.Status.PlatformStatus.Type {
case configapiv1.OpenStackPlatformType:
isSwiftEnabled, err := swift.IsSwiftEnabled(c.listers)
if err != nil {
return err
}
if !isSwiftEnabled {
err = c.createPVC(corev1.ReadWriteOnce)
if err != nil {
return err
}

rolloutStrategy = appsapi.RecreateDeploymentStrategyType
}
}

cr = &imageregistryv1.Config{
ObjectMeta: metav1.ObjectMeta{
Name: defaults.ImageRegistryResourceName,
Expand All @@ -73,7 +105,7 @@ func (c *Controller) Bootstrap() error {
Storage: imageregistryv1.ImageRegistryConfigStorage{},
Replicas: 1,
HTTPSecret: fmt.Sprintf("%x", string(secretBytes[:])),
RolloutStrategy: string(appsapi.RollingUpdateDeploymentStrategyType),
RolloutStrategy: string(rolloutStrategy),
},
Status: imageregistryv1.ImageRegistryStatus{},
}
Expand All @@ -94,3 +126,46 @@ func (c *Controller) Bootstrap() error {

return nil
}

func (c *Controller) createPVC(accessMode corev1.PersistentVolumeAccessMode) error {
claimName := defaults.PVCImageRegistryName

// Check that the claim does not exist before creating it
claim, err := c.clients.Core.PersistentVolumeClaims(defaults.ImageRegistryOperatorNamespace).Get(claimName, metav1.GetOptions{})
if err != nil {
if !errors.IsNotFound(err) {
return err
}

// "standard" is the default StorageClass name, that was provisioned by the cloud provider
storageClassName := "standard"

claim = &corev1.PersistentVolumeClaim{
ObjectMeta: metav1.ObjectMeta{
Name: claimName,
Namespace: defaults.ImageRegistryOperatorNamespace,
Annotations: map[string]string{
pvc.PVCOwnerAnnotation: "true",
},
},
Spec: corev1.PersistentVolumeClaimSpec{
AccessModes: []corev1.PersistentVolumeAccessMode{
accessMode,
},
Resources: corev1.ResourceRequirements{
Requests: corev1.ResourceList{
corev1.ResourceStorage: resource.MustParse("100Gi"),
},
},
StorageClassName: &storageClassName,
},
}

_, err = c.clients.Core.PersistentVolumeClaims(defaults.ImageRegistryOperatorNamespace).Create(claim)
if err != nil {
return err
}
}

return nil
}
8 changes: 4 additions & 4 deletions pkg/storage/pvc/pvc.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import (
const (
rootDirectory = "/registry"
randomSecretSize = 32
pvcOwnerAnnotation = "imageregistry.openshift.io"
PVCOwnerAnnotation = "imageregistry.openshift.io"
)

type driver struct {
Expand Down Expand Up @@ -155,7 +155,7 @@ func (d *driver) createPVC(cr *imageregistryv1.Config) (*corev1.PersistentVolume
Name: d.Config.Claim,
Namespace: d.Namespace,
Annotations: map[string]string{
pvcOwnerAnnotation: "true",
PVCOwnerAnnotation: "true",
},
},
Spec: corev1.PersistentVolumeClaimSpec{
Expand All @@ -181,7 +181,7 @@ func (d *driver) CreateStorage(cr *imageregistryv1.Config) error {
)

if len(d.Config.Claim) == 0 {
d.Config.Claim = fmt.Sprintf("%s-storage", defaults.ImageRegistryName)
d.Config.Claim = defaults.PVCImageRegistryName

// If there is no name and there is no PVC, then we will create a PVC.
// If PVC is there and it was created by us, then just start using it again.
Expand Down Expand Up @@ -236,7 +236,7 @@ func (d *driver) RemoveStorage(cr *imageregistryv1.Config) (retriable bool, err
}

func pvcIsCreatedByOperator(claim *corev1.PersistentVolumeClaim) (exist bool) {
_, exist = claim.Annotations[pvcOwnerAnnotation]
_, exist = claim.Annotations[PVCOwnerAnnotation]
return
}

Expand Down
13 changes: 12 additions & 1 deletion pkg/storage/storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (

configapiv1 "github.com/openshift/api/config/v1"
imageregistryv1 "github.com/openshift/api/imageregistry/v1"
"github.com/openshift/cluster-image-registry-operator/defaults"
regopclient "github.com/openshift/cluster-image-registry-operator/pkg/client"
"github.com/openshift/cluster-image-registry-operator/pkg/envvar"
"github.com/openshift/cluster-image-registry-operator/pkg/storage/azure"
Expand Down Expand Up @@ -155,7 +156,17 @@ func GetPlatformStorage(listers *regopclient.Listers) (imageregistryv1.ImageRegi
case configapiv1.GCPPlatformType:
cfg.GCS = &imageregistryv1.ImageRegistryConfigStorageGCS{}
case configapiv1.OpenStackPlatformType:
cfg.Swift = &imageregistryv1.ImageRegistryConfigStorageSwift{}
isSwiftEnabled, err := swift.IsSwiftEnabled(listers)
if err != nil {
return imageregistryv1.ImageRegistryConfigStorage{}, err
}
if !isSwiftEnabled {
cfg.PVC = &imageregistryv1.ImageRegistryConfigStoragePVC{
Claim: defaults.PVCImageRegistryName,
}
} else {
cfg.Swift = &imageregistryv1.ImageRegistryConfigStorageSwift{}
}

// Unknown platforms or LibVirt: we configure image registry using
// EmptyDir storage.
Expand Down
22 changes: 18 additions & 4 deletions pkg/storage/swift/swift.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,20 @@ func replaceEmpty(a string, b string) string {
return a
}

// IsSwiftEnabled checks if Swift service is available for OpenStack platform
func IsSwiftEnabled(listers *regopclient.Listers) (bool, error) {
driver := NewDriver(&imageregistryv1.ImageRegistryConfigStorageSwift{}, listers)
_, err := driver.getSwiftClient()
if err != nil {
// ErrEndpointNotFound means that Swift is not available
if _, ok := err.(*gophercloud.ErrEndpointNotFound); ok {
return false, nil
}
return false, err
}
return true, nil
}

// GetConfig reads credentials
func GetConfig(listers *regopclient.Listers) (*Swift, error) {
cfg := &Swift{}
Expand Down Expand Up @@ -129,7 +143,7 @@ func GetConfig(listers *regopclient.Listers) (*Swift, error) {
}

// getSwiftClient returns a client that allows to interact with the OpenStack Swift service
func (d *driver) getSwiftClient(cr *imageregistryv1.Config) (*gophercloud.ServiceClient, error) {
func (d *driver) getSwiftClient() (*gophercloud.ServiceClient, error) {
cfg, err := GetConfig(d.Listers)
if err != nil {
return nil, err
Expand Down Expand Up @@ -288,7 +302,7 @@ func (d *driver) containerExists(client *gophercloud.ServiceClient, containerNam
}

func (d *driver) StorageExists(cr *imageregistryv1.Config) (bool, error) {
client, err := d.getSwiftClient(cr)
client, err := d.getSwiftClient()
if err != nil {
util.UpdateCondition(cr, defaults.StorageExists, operatorapi.ConditionUnknown, "Could not connect to registry storage", err.Error())
return false, err
Expand Down Expand Up @@ -318,7 +332,7 @@ func (d *driver) StorageChanged(cr *imageregistryv1.Config) bool {
}

func (d *driver) CreateStorage(cr *imageregistryv1.Config) error {
client, err := d.getSwiftClient(cr)
client, err := d.getSwiftClient()
if err != nil {
util.UpdateCondition(cr, defaults.StorageExists, operatorapi.ConditionFalse, err.Error(), err.Error())
return err
Expand Down Expand Up @@ -396,7 +410,7 @@ func (d *driver) RemoveStorage(cr *imageregistryv1.Config) (bool, error) {
return false, nil
}

client, err := d.getSwiftClient(cr)
client, err := d.getSwiftClient()
if err != nil {
return false, err
}
Expand Down
31 changes: 31 additions & 0 deletions pkg/storage/swift/swift_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"net/http"
"testing"

"github.com/gophercloud/gophercloud"
th "github.com/gophercloud/gophercloud/testhelper"

corev1 "k8s.io/api/core/v1"
Expand Down Expand Up @@ -609,3 +610,33 @@ func TestSwiftEndpointTypeObjectStore(t *testing.T) {
th.AssertNoErr(t, err)
th.AssertEquals(t, true, res)
}

func TestSwiftIsNotAvailable(t *testing.T) {
th.SetupHTTP()
defer th.TeardownHTTP()
// Swift endpoint is not registered
handleAuthentication(t, "INVALID")

th.Mux.HandleFunc("/"+container, func(w http.ResponseWriter, r *http.Request) {
th.TestMethod(t, r, "HEAD")
th.TestHeader(t, r, "Accept", "application/json")
w.Header().Set("Accept-Ranges", "bytes")
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.Header().Set("Date", "Wed, 17 Aug 2016 19:25:43 GMT")
w.Header().Set("X-Container-Bytes-Used", "100")
w.Header().Set("X-Container-Object-Count", "4")
w.Header().Set("X-Container-Read", "test")
w.Header().Set("X-Container-Write", "test2,user4")
w.Header().Set("X-Timestamp", "1471298837.95721")
w.Header().Set("X-Trans-Id", "tx554ed59667a64c61866f1-0057b4ba37")
w.Header().Set("X-Storage-Policy", "test_policy")
w.WriteHeader(http.StatusNoContent)
})

d, _ := mockConfig(false, th.Endpoint()+"v3", MockUPISecretNamespaceLister{})

_, err := d.getSwiftClient()
// if Swift endpoint is not registered, getSwiftClient should return ErrEndpointNotFound
_, ok := err.(*gophercloud.ErrEndpointNotFound)
th.AssertEquals(t, true, ok)
}