diff --git a/pkg/rhcos/openstack.go b/pkg/rhcos/openstack.go index 04044cb53e6..98bd8640e7b 100644 --- a/pkg/rhcos/openstack.go +++ b/pkg/rhcos/openstack.go @@ -3,6 +3,7 @@ package rhcos import ( "context" "net/url" + "strings" "github.com/pkg/errors" ) @@ -25,9 +26,16 @@ func OpenStack(ctx context.Context) (string, error) { return "", err } - // Attach uncompressed sha256 checksum to the URL - checkSuffix := "?sha256=" + meta.Images.OpenStack.UncompressedSHA256 - baseURL := base.ResolveReference(relOpenStack).String() + checkSuffix + baseURL := base.ResolveReference(relOpenStack).String() + + // Attach sha256 checksum to the URL. If the file has the ".gz" extension, then the + // data is compressed and we use SHA256 value; otherwise we work with uncompressed + // data and therefore need UncompressedSHA256. + if strings.HasSuffix(baseURL, ".gz") { + baseURL += "?sha256=" + meta.Images.OpenStack.SHA256 + } else { + baseURL += "?sha256=" + meta.Images.OpenStack.UncompressedSHA256 + } // Check that we have generated a valid URL _, err = url.ParseRequestURI(baseURL) diff --git a/pkg/tfvars/openstack/openstack.go b/pkg/tfvars/openstack/openstack.go index 7dc329a9702..9d428769b1c 100644 --- a/pkg/tfvars/openstack/openstack.go +++ b/pkg/tfvars/openstack/openstack.go @@ -2,8 +2,13 @@ package openstack import ( + "compress/gzip" "encoding/json" "fmt" + "io" + "net/url" + "os" + "strings" "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens" "github.com/gophercloud/gophercloud/openstack/imageservice/v2/images" @@ -11,6 +16,7 @@ import ( "github.com/openshift/installer/pkg/rhcos" "github.com/openshift/installer/pkg/tfvars/internal/cache" "github.com/pkg/errors" + "github.com/sirupsen/logrus" "sigs.k8s.io/cluster-api-provider-openstack/pkg/apis/openstackproviderconfig/v1alpha1" ) @@ -63,7 +69,32 @@ func TFVars(masterConfig *v1alpha1.OpenstackProviderSpec, cloud string, external return nil, err } - cfg.BaseImageLocalFilePath = localFilePath + // Compressed image support was added to OpenStack Glance only in Train release. Unfortunately previous + // versions do not have this feature, so we have to check whether or not we need to decompress the file. + // For more information: https://docs.openstack.org/glance/latest/user/formats.html + // TODO(mfedosin): Allow to skip this step if Glance supports compressed images. + baseImageURL, err := url.ParseRequestURI(baseImage) + // If the file has ".gz" extension, then its data is compressed + if strings.HasSuffix(baseImageURL.Path, ".gz") { + localFilePathUncompressed := localFilePath + ".uncompressed" + + // Do nothing if we already have the uncompressed file in cache, otherwise decompress the data + _, err = os.Stat(localFilePathUncompressed) + if err != nil { + if os.IsNotExist(err) { + logrus.Infof("Decompress image data from %v to %v", localFilePath, localFilePathUncompressed) + err = decompressFile(localFilePath, localFilePathUncompressed) + if err != nil { + return nil, err + } + } else { + return nil, err + } + } + cfg.BaseImageLocalFilePath = localFilePathUncompressed + } else { + cfg.BaseImageLocalFilePath = localFilePath + } } else { // Not a URL -> use baseImage value as an overridden Glance image name. // Need to check if this image exists and there are no other images with this name. @@ -99,6 +130,32 @@ func TFVars(masterConfig *v1alpha1.OpenstackProviderSpec, cloud string, external return json.MarshalIndent(cfg, "", " ") } +// decompressFile decompresses data in the cache +func decompressFile(src, dest string) error { + gzipfile, err := os.Open(src) + if err != nil { + return err + } + + reader, err := gzip.NewReader(gzipfile) + defer reader.Close() + if err != nil { + return err + } + + writer, err := os.Create(dest) + defer writer.Close() + if err != nil { + return err + } + + if _, err = io.Copy(writer, reader); err != nil { + return err + } + + return nil +} + func validateOverriddenImageName(imageName, cloud string) error { opts := &clientconfig.ClientOpts{ Cloud: cloud,