How to use Vultr Object Storage in Go

Updated on November 21, 2023
How to use Vultr Object Storage in Go header image

Introduction

Vultr Object Storage is a highly scalable, S3-Compatible cloud object storage solution for storing files or objects. This guide explains how to use Vultr Object Storage in Go with GoVultr v2, and how to manage Buckets inside Object Storage with AWS SDK for Go.

Prerequisites

You will need:

  • A computer workstation running on Linux, Windows, or macOS
  • The latest version of Go installed on your computer workstation

These examples use the Linux $ prompt, but the commands also work on Mac and Windows.

Manage Vultr Object Storage with Go

  1. Generate an API key from your API Settings by enabling your API.

    Warning: Keep your API key secured. Your API key has access to your entire Vultr account, and publicly exposing your API key may compromise your account.

  2. Create a new project folder.

     $ mkdir ObjectStorage
  3. Change the directory to the new folder.

     $ cd ObjectStorage
  4. Initialize the module.

     $ go mod init ObjectStorage
  5. Install GoVultr v2.

     $ go get -u github.com/vultr/govultr/v2
  6. Install oAuth2 for Go.

     $ go get golang.org/x/oauth2
  7. Set your API key as an environment variable. Replace YOUR-API-KEY-HERE with your API Key found on your API Settings.

    Linux and macOS workstations run:

     $ export VULTR_API_KEY=YOUR-API-KEY-HERE

    Windows workstations run:

     C:\> setx VULTR_API_KEY "YOUR-API-KEY-HERE"
  8. Use your preferred plain text editor to create and edit main.go. Linux users may use nano, while Mac and Windows users might choose TextEdit or Notepad.

     $ nano main.go
  9. Paste the following code into main.go, which sets up the main package and imports all the required packages.

     package main
    
     import (
         "bufio"
          "context"
          "fmt"
          "log"
          "os"
          "strings"
          "time"
    
          "github.com/vultr/govultr/v2"
          "golang.org/x/oauth2"
     )
  10. Paste the following code into main.go, which declares the global variables.

     var (
          apiKey       = os.Getenv("VULTR_API_KEY")
          vc           *govultr.Client
          ctx          = context.Background()
          input        int
          input2       string
          loop         string
          objStorageID string
     )
  11. Paste the following code into main.go, which initializes oAuth2 config, TokenSource, and the Vultr Client.

     func init() {
         config := &oauth2.Config{}
         ts := config.TokenSource(ctx, &oauth2.Token{AccessToken: apiKey})
         vc = govultr.NewClient(oauth2.NewClient(ctx, ts))
     }

Object Storage Management with GoVultr

Populate your main.go file with each of these functions. Each function is for a specific object storage operation.

Function: listClusters()

This function lists all object storage clusters. It stops the program if there are errors when listing clusters or if no clusters are found.

func listClusters() {
     clusterList, meta, err := vc.ObjectStorage.ListCluster(ctx, nil)
     if err != nil {
          log.Panicf("Error listing clusters: %s", err)
     }
     if meta.Total <= 0 {
          log.Panic("There are no clusters found to create an Object Storage.")
     }
     log.Printf("List of All Clusters: %+v", clusterList)
}

Function: createObjStorage()

This function creates an object storage in your selected cluster, then prints its complete information once it becomes active. It stops the program if there are any errors when creating an object storage.

This guide uses ClusterID 2, which is Vultr's New Jersey Object Storage location, ewr1.vultrobjects.com.

func createObjStorage() {
     var objStorageName string
     clusterID := 2
     fmt.Print("Enter Your Desired Object Storage Name: ")
     scanner := bufio.NewScanner(os.Stdin)
     for scanner.Scan() {
          objStorageName = scanner.Text()
          fmt.Print()
          if objStorageName != "" {
               break
          }
     }
     objStorageNew, errn := vc.ObjectStorage.Create(ctx, clusterID, objStorageName)
     if errn != nil {
          log.Panicf("Error creating storage: %s", errn)
     } else {
          log.Printf("Succesfully created an Object Storage with ID: %s", objStorageNew.ID)
     }
     for {
          objStorageNewInfo, erri := vc.ObjectStorage.Get(ctx, objStorageNew.ID)
          if erri != nil {
               log.Panicf("Error getting Object Storage Information: %s", erri)
          }
          if objStorageNewInfo.Status == "active" {
               log.Print("Your Object Storage is now Active.")
               log.Printf("Object Storage Information: %+v", objStorageNewInfo)
               log.Println("S3 Credentials: ")
               log.Printf("Hostname: %s | Access Key: %s | Secret Key: %s", objStorageNewInfo.S3Hostname, objStorageNewInfo.S3AccessKey, objStorageNewInfo.S3SecretKey)
               break
          }
          log.Printf("The Object Storage is currently %s. Waiting another ten seconds until it becomes active.", objStorageNewInfo.Status)
          time.Sleep(time.Second * 10)
     }
}

The function requires one user input: The user's desired name for the object storage. It then creates new object storage and checks the status of the newly created object storage every ten seconds until it becomes active. After the object storage's status becomes active, it displays its complete information.

Functions: listObjStorage1() and listObjStorage2()

These functions list all object storage in your account. There are two ways to display your object storage information.

First: list all object storage one by one, then display the data in JSON format.

func listObjStorage1() {
     objStorageList, meta, err := vc.ObjectStorage.List(ctx, nil)
     log.Printf("The Total Number of Object Storage: %d", meta.Total)
     for x := 0; x != len(objStorageList); x++ {
          log.Printf("Object Storage #%d:", x+1)
          log.Printf("List of Object Storage: %+v", objStorageList[x])
          log.Println()
     }
     if err != nil {
          log.Panicf("Error listing Object Storage: %s", err)
     }
}

This function displays the total number of active object storage in your account and their complete information in JSON format.

Second: list all object storage one by one, then display the data of each object storage by key-value pairs.

func listObjStorage2() {
     objStorageList, meta, err := vc.ObjectStorage.List(ctx, nil)
     log.Printf("The Total Number of Object Storage: %d", meta.Total)
     for x := 0; x != len(objStorageList); x++ {
          log.Printf("Object Storage #%d:", x+1)
          log.Printf("Date Created: %s", objStorageList[x].DateCreated)
          log.Printf("Object Storage ID: %s", objStorageList[x].ID)
          log.Printf("Object Storage Label: %s", objStorageList[x].Label)
          log.Printf("Object Storage Location: %s", objStorageList[x].Location)
          log.Printf("Object Storage Region: %s", objStorageList[x].Region)
          log.Printf("Object Storage Hostname: %s", objStorageList[x].S3Hostname)
          log.Printf("Object Storage Access Key: %s", objStorageList[x].S3AccessKey)
          log.Printf("Object Storage Secret Key: %s", objStorageList[x].S3SecretKey)
          log.Printf("Object Storage Status: %s", objStorageList[x].Status)
          log.Printf("Object Storage Cluster ID: %d", objStorageList[x].ObjectStoreClusterID)
          log.Println()
     }
     if err != nil {
          log.Panicf("Error listing Object Storage: %s", err)
     }
}

This function displays the total number of active object storage in your account and their complete information. The Object Storage's ID is required when you want to manipulate Object Storage.

Function: getObjStorage()

This function displays the complete information of a specific object storage or stops the program if there is an error in getting your object storage's information.

func getObjStorage() {
     fmt.Println("To Get your Object Storage's ID, List all of your Object Storage.")
     fmt.Print("Enter Your Object Storage's ID to get its Full Information (e.g. cb676a46-66fd-4dfb-b839-443f2e6c0b60): ")
     fmt.Scan(&objStorageID)
     objStorageGet, err := vc.ObjectStorage.Get(ctx, objStorageID)
     log.Printf("Full information of Object Storage with an ID \"%s\".", objStorageID)
     log.Printf("Object Storage ID: %s", objStorageGet.ID)
     log.Printf("Date Created: %s", objStorageGet.DateCreated)
     log.Printf("Label: %s", objStorageGet.Label)
     log.Printf("Location: %s", objStorageGet.Location)
     log.Printf("Region: %s", objStorageGet.Region)
     log.Printf("S3 Hostname: %s", objStorageGet.S3Hostname)
     log.Printf("S3 Access Key: %s", objStorageGet.S3AccessKey)
     log.Printf("S3 Secret Key: %s", objStorageGet.S3SecretKey)
     log.Printf("Status: %s", objStorageGet.Status)
     log.Printf("Cluster ID: %d", objStorageGet.ObjectStoreClusterID)
     log.Println()
     if err != nil {
          log.Panicf("Error Getting Object Storage that has an ID %s: %s", objStorageID, err)
     }
}

This function requires one user input: Your object storage's ID, which you can find when you list all your object storage.

Function: delObjStorage()

This function deletes a specific object storage.

func delObjStorage() {
     fmt.Println("To Get your Object Storage's ID, List all of your Object Storage.")
     fmt.Print("Enter the ID of the Object Storage that you want to Delete (e.g. cb676a46-66fd-4dfb-b839-443f2e6c0b60): ")
     fmt.Scan(&objStorageID)
     objStorageDel := vc.ObjectStorage.Delete(ctx, objStorageID)
     if objStorageDel == nil {
          log.Printf("Successfully deleted object storage with an ID \"%s\"", objStorageID)
     }
}

This function requires one user input: The ID of the object storage that you want to delete.

Function: updateObjectStorage()

This function updates or changes the label of a specific object storage.

func updateObjectStorage() {
     var newLabel string
     fmt.Println("To Get your Object Storage's ID, List all of your Object Storage.")
     fmt.Print("Enter the ID of the Object Storage that you want to Update the Label (e.g. cb676a46-66fd-4dfb-b839-443f2e6c0b60): ")
     fmt.Scan(&objStorageID)
     fmt.Print("Enter Your Desired New Object Storage Label: ")
     scanner := bufio.NewScanner(os.Stdin)
     for scanner.Scan() {
          newLabel = scanner.Text()
          fmt.Print()
          if newLabel != "" {
               break
          }
     }
     objStorageUpdate := vc.ObjectStorage.Update(ctx, objStorageID, newLabel)
     if objStorageUpdate == nil {
          log.Printf("Succesfully updated the label of the object storage with an ID of: \"%s\"", objStorageID)
     }
     fmt.Print()
}

This function requires two user inputs: Your object storage's ID, which you can find when you list all your object storage, and your preferred new object storage label.

Function regenKeys()

This function will regenerate a new Access Key and Secret Key of a specific object storage.

func regenKeys() {
     fmt.Println("To Get your Object Storage's ID, List all of your Object Storage.")
     fmt.Print("Enter the ID of the Object Storage that you want to Regenerate Keys (e.g. cb676a46-66fd-4dfb-b839-443f2e6c0b60): ")
     fmt.Scan(&objStorageID)
     objStorageNewKeys, err := vc.ObjectStorage.RegenerateKeys(ctx, objStorageID)
     log.Print("Successfully Regenerated new S3 Credentials.")
     log.Printf("Your Object Storage's New S3 Credentials are: %v", objStorageNewKeys)
     log.Printf("S3 Hostname: %s", objStorageNewKeys.S3Hostname)
     log.Printf("S3 Access Key: %s", objStorageNewKeys.S3AccessKey)
     log.Printf("S3 Secret Key: %s", objStorageNewKeys.S3SecretKey)
     if err != nil {
          log.Panicf("Error Regenerating New S3 Credentials: %v", err)
     }
}

This function requires one user input: the ID of the object storage that you want to regenerate the keys.

Main Function

This function asks the user what object storage operation they would like to perform, then calls a specific function based on the user input.

func main() {
     for {
          fmt.Println("Input '1' to List All Object Storage Clusters.")
          fmt.Println("Input '2' to Create an Object Storage.")
          fmt.Println("Input '3' to List All Object Storage (Less Organized).")
          fmt.Println("Input '4' to List All Object Storage (More Organized).")
          fmt.Println("Input '5' to Get the Full Information of your Object Storage.")
          fmt.Println("Input '6' to Delete an Object Storage.")
          fmt.Println("Input '7' to Update an Object Storage's Label.")
          fmt.Println("Input '8' to Regenerate Access Key and Secret Key of your Object Storage.")
          fmt.Print("Your Input: ")
          fmt.Scan(&input)
          switch input {
          case 1:
               listClusters()
          case 2:
               createObjStorage()
          case 3:
               listObjStorage1()
          case 4:
               listObjStorage2()
          case 5:
               getObjStorage()
          case 6:
               delObjStorage()
          case 7:
               updateObjectStorage()
          case 8:
               regenKeys()
          default:
               fmt.Println("Invalid Input! Please try again.")
               continue
          }

          fmt.Print("Do you want to rerun the program? (y/n): ")
          fmt.Scan(&input2)
          loop = strings.ToLower(input2)
          if loop == "n" {
               fmt.Println("Closing the Program...")
               time.Sleep(2 * time.Second)
               break
          } else if loop == "y" {
               continue
          } else {
               log.Fatal("Invalid Input! Closing the Program.")
               time.Sleep(2 * time.Second)
          }
     }
}

Build the Object Storage Project

After you have populated the main.go file with the example code, save and close the file. Then, compile the code into a binary executable file.

$ go build

Run the executable file, then select which object storage operation you like to perform.

$ ./ObjectStorage

Bucket Operations

To manage your Buckets, use the AWS SDK for Go.

Set up the Bucket Project Environment

  1. Create a new project folder.

     $ mkdir Bucket
  2. Change the directory to the new folder.

     $ cd Bucket
  3. Initialize the module.

     $ go mod init Bucket
  4. Install AWS SDK for Go.

     $ go get github.com/aws/aws-sdk-go
  5. Install mimetype. This package is for setting the Content-Type of each file to be uploaded.

     $ go get github.com/gabriel-vasile/mimetype
  6. Get your Object Storage's Access key and Secret Key from Vultr's website, or list all your object storage using the ObjectStorage program above.

  7. Set your Object Storage's Access key and Secret key as environment variables. Replace YOUR-ACCESS-KEY-HERE and YOUR-SECRET-KEY-HERE with your object storage's Access key and Secret key.

    Linux and macOS workstations run:

     $ export ACCESS_KEY=YOUR-ACCESS-KEY-HERE
     $ export SECRET_KEY=YOUR-SECRET-KEY-HERE

    Windows workstations run:

     C:\> setx ACCESS_KEY "YOUR-ACCESS-KEY-HERE"
     C:\> setx SECRET_KEY "YOUR-SECRET-KEY-HERE"
  8. Use your preferred plain text editor to create and edit main.go. Linux users may use nano, while Mac and Windows users might choose TextEdit or Notepad.

     $ nano main.go
  9. Paste the following code into main.go, which sets up the main package and imports all the required packages.

     package main
    
     import (
          "fmt"
          "io/ioutil"
          "log"
          "os"
          "path"
          "regexp"
          "strings"
          "time"
    
          "github.com/aws/aws-sdk-go/aws"
          "github.com/aws/aws-sdk-go/aws/awserr"
          "github.com/aws/aws-sdk-go/aws/credentials"
          "github.com/aws/aws-sdk-go/aws/session"
          "github.com/aws/aws-sdk-go/service/s3"
          "github.com/gabriel-vasile/mimetype"
     )
  10. Paste the following code into main.go, which declares the global variables.

     var (
          s3Vultr    *s3.S3
          accessKey  = os.Getenv("ACCESS_KEY")
          secretKey  = os.Getenv("SECRET_KEY")
          bucketName string
          directory  string
          filename   string
          input      int
          input2     string
          loop       string
     )
  11. Paste the following code into main.go, which initializes the Vultr S3 session.

     func init() {
         s3Vultr = s3.New(session.Must(session.NewSession(&aws.Config{
             Region:      aws.String("ewr"),
             Credentials: credentials.NewStaticCredentials(accessKey, secretKey, ""),
             Endpoint:    aws.String("https://ewr1.vultrobjects.com/"),
         })))
     }

Bucket Operations using AWS SDK for Go

Populate your main.go file with each of these functions. Each function is for a specific bucket operation.

Function: listAllBuckets()

This function lists all the buckets in your object storage.

func listAllBuckets() (ret *s3.ListBucketsOutput) {
     ret, err := s3Vultr.ListBuckets(&s3.ListBucketsInput{})
     if err != nil {
          panic(err)
     }
     return ret
}

Function: createBucket()

This function has two helper functions, nameCheck() and newBucket(). It creates a bucket in your object storage or stops the program if the bucket name already exists. It requires one user input: Your preferred name for your bucket. If your input bucket name does not meet the criteria for naming a bucket, it will ask you to reinput another bucket name.

func nameCheck(str string) bool {
     checker := regexp.MustCompile(`^[a-z0-9-]*$`).MatchString(str)
     alphanumeric := "abcdefghijklmnopqrstuvwxyz1234567890"
     if checker && strings.Contains(alphanumeric, string(str[0])) && len(str) >= 3 && len(str) <= 63 {
          return true
     } else {
          return false
     }
}

func newBucket() (ret *s3.CreateBucketOutput) {
     _, err := s3Vultr.CreateBucket(&s3.CreateBucketInput{
          Bucket: aws.String(bucketName),
          CreateBucketConfiguration: &s3.CreateBucketConfiguration{
               LocationConstraint: aws.String(""),
          },
     })
     if awsError, ok := err.(awserr.Error); ok {
          if awsError.Code() == s3.ErrCodeBucketAlreadyExists {
               log.Fatalf("Bucket %q already exists. Error: %v", bucketName, awsError.Code())
          }
     } else {
          log.Printf("Successfully created bucket %q", bucketName)
     }
     return ret
}

func createBucket() (ret *s3.CreateBucketOutput) {
     for {
          fmt.Println("Bucket names are unique to their location and must meet the following criteria:")
          fmt.Println("Only lowercase and starts with a letter or number. No spaces.")
          fmt.Println("Bucket name may contain dashes")
          fmt.Println("Must be between 3 and 63 characters long.")
          fmt.Print("Enter your preferred Name for the Bucket: ")
          fmt.Scan(&bucketName)
          if nameCheck(bucketName) {
               break
          } else {
               fmt.Printf("%q does not meet the criteria above. Please try again.", bucketName)
               fmt.Print("\n\n")
               continue
          }
     }
     bucketList := listAllBuckets()
     if len(bucketList.Buckets) != 0 {
          for _, bucket := range bucketList.Buckets {
               if bucketName == *bucket.Name {
                    log.Fatalf("Bucket %q already exists and is owned by you.", bucketName)
               } else {
                    newBucket()
                    break
               }
          }
     } else {
          newBucket()
     }
     return ret
}

The createBucket() function checks if your input bucket name already exists and is owned by you, then it calls the nameCheck() function and the newBucket() function.

The nameCheck() function checks the user's input bucket name to see if it meets the criteria for naming a bucket and asks again if it does not. It uses the regexp package to check if the input only contains lowercase letters, numbers, and dashes. To check if the input starts with a letter or a number, it uses the strings package's strings.Contains(). It also checks if the input length is between 3 and 63 characters long.

The newBucket() function tries to create a new bucket if the input bucket name does not exist and returns an error otherwise.

Function: uploadObject()

This function uploads a file or an object to a specific bucket. It requires three user inputs: The bucket name where you want to store the file, the path or directory where you want to put the files in the bucket, and last is the path of the file that you want to upload.

func uploadObject() (ret *s3.PutObjectOutput) {
     fmt.Print("Enter the name of the bucket where you want to upload the File/Object: ")
     fmt.Scan(&bucketName)
     fmt.Print("Enter the Path or Directory where you want to upload the File/Object in the bucket: (e.g., assets/css/): ")
     fmt.Scan(&directory)
     fmt.Print("Enter the Path to the file that you want to upload (e.g., css/styles.css): ")
     fmt.Scan(&filename)

     f, err := os.Open(filename)
     if err != nil {
          panic(err)
     }
     var mtype2 string
     if strings.Contains(filename, ".css") {
          mtype2 = "text/css"
     } else {
          mtype, errmime := mimetype.DetectFile(filename)
          if errmime != nil {
               log.Fatalf("Error getting Content-Type: %v", errmime)
          }
          mtype2 = mtype.String()
     }

     log.Println("Uploading Object:", filename)
     ret, err = s3Vultr.PutObject(&s3.PutObjectInput{
          Body:        f,
          Bucket:      aws.String(bucketName),
          Key:         aws.String(path.Join(directory, strings.Split(filename, "/")[strings.Count(filename, "/")])),
          ACL:         aws.String("public-read"),
          ContentType: aws.String(mtype2),
     })

     if err != nil {
          panic(err)
     } else {
          log.Printf("File %q was Uploaded Successfully.", filename)
     }
     return ret
}

Your file's Canned Access Control List (ACL) is set to private by default, which means no one except you has access to it. To serve your files appropriately, you must Grant Public Read Access and set your file's content type correctly. The field ACL with the value public-read grants all users a READ access and the owner a FULL CONTROL. When you upload files to your bucket, the default Content Type is usually application/octet-stream. To automatically set your file's content type, the program used the mimetype package to detect each file's MIME type or content type. See this list of supported MIME types for your reference. Notice that CSS is not in the list of supported MIME types, so it is set using strings.Contains(). If the filename has a .css extension, it sets the content type to text/css.

Function: listObjects()

This function lists all files or objects in a bucket. It requires one user input: the bucket's name to list its files.

func listObjects() (ret *s3.ListObjectsV2Output) {
     fmt.Print("Enter the name of the bucket to list its file/s: ")
     fmt.Scan(&bucketName)
     ret, err := s3Vultr.ListObjectsV2(&s3.ListObjectsV2Input{
          Bucket: aws.String(bucketName),
     })

     if err != nil {
          panic(err)
     }
     return ret
}

Function: getObject()

This function downloads a file or an object from a bucket into the same directory where your main.go or binary executable file is. It requires three user inputs: The bucket name, the directory or path of the file, and the name of the file you want to download.

func getObject() {
     fmt.Print("Enter the name of the bucket that contains the file that you want to download: ")
     fmt.Scan(&bucketName)
     fmt.Print("Enter the Path or Directory of the file (e.g. assets/css/): ")
     fmt.Scan(&directory)
     fmt.Print("Enter the name of the file that you want to download(e.g., styles.css): ")
     fmt.Scan(&filename)
     log.Println("Downloading: ", filename)

     ret, err := s3Vultr.GetObject(&s3.GetObjectInput{
          Bucket: aws.String(bucketName),
          Key:    aws.String(path.Join(directory, strings.Split(filename, "/")[strings.Count(filename, "/")])),
     })

     if err != nil {
          panic(err)
     }

     body, _ := ioutil.ReadAll(ret.Body)
     err = ioutil.WriteFile(filename, body, 0644)
     if err != nil {
          panic(err)
     }
     log.Println("File Downloaded Successfully.")
}

This function reads the file inside a bucket using ioutil.ReadAll(), then writes the returned data using ioutil.WriteFile(). It saves the file as filename, with a 644 permission which means the owner has Read and Write access, while the group and others only have Read access.

Function: deleteObject()

This function deletes a file or an object from a bucket. It requires three user inputs: the name of the bucket that contains the file you want to delete, the path or directory of the file in the bucket, and the name of the file you want to delete.

func deleteObject() (ret *s3.DeleteObjectOutput) {
     fmt.Print("Enter the name of the bucket that contains the file that you want to delete: ")
     fmt.Scan(&bucketName)
     fmt.Print("Enter the Path or Directory of the file in the bucket (e.g., assets/css/): ")
     fmt.Scan(&directory)
     fmt.Print("Enter the name of the file that you want to delete (e.g., styles.css): ")
     fmt.Scan(&filename)
     log.Println("Deleting: ", filename)
     ret, err := s3Vultr.DeleteObject(&s3.DeleteObjectInput{
          Bucket: aws.String(bucketName),
          Key:    aws.String(path.Join(directory, strings.Split(filename, "/")[strings.Count(filename, "/")])),
     })

     if err != nil {
          panic(err)
     } else {
          log.Printf("%q deleted Successfully", filename)
     }
     return ret
}

Function: deleteAllObjects()

This function deletes all files or objects in a bucket. It requires one user input: The Name of the Bucket that you want to empty. This function can be useful when you want to delete a bucket since you must delete all of its contents before you can delete it.

func deleteAllObjects() {
     fmt.Scan(&bucketName)
     objectList, errList := s3Vultr.ListObjectsV2(&s3.ListObjectsV2Input{
          Bucket: aws.String(bucketName),
     })
     if errList != nil {
          log.Fatalf("Error Listing objects: %v", errList)
     }
     for _, object := range objectList.Contents {
          log.Printf("Deleting %v", *object.Key)
          _, err := s3Vultr.DeleteObject(&s3.DeleteObjectInput{
               Bucket: aws.String(bucketName),
               Key:    aws.String(*object.Key),
          })
          log.Printf("%v Deleted Successfully", *object.Key)
          if err != nil {
               log.Fatalf("Error Deleteing Objects.")
          }
     }
     log.Println("All Files deleted successfully.")
}

This function lists all the bucket's contents first, and then it gets the Key of each object to be used to delete each file.

Function: deleteBucket()

This function deletes a bucket from your object storage. It requires one user input: the name of the bucket you want to delete.

func deleteBucket() (ret *s3.DeleteBucketOutput) {
     deleteAllObjects()
     ret, err := s3Vultr.DeleteBucket(&s3.DeleteBucketInput{
          Bucket: aws.String(bucketName),
     })
     if err != nil {
          if awsError, ok := err.(awserr.Error); ok {
               switch awsError.Code() {
               case s3.ErrCodeNoSuchBucket:
                    log.Fatalf("No Bucket exists with the name '%s'", bucketName)
                    log.Println()
               default:
                    panic(err)
               }
          }
     }
     log.Printf("Bucket %q deleted Successfully.", bucketName)
     return ret
}

This function calls the deleteAllObjects() first to delete all of its contents because you must empty the bucket before you can delete it.

Bucket Main Function

The main() function asks the user what bucket operation they would like to perform, then calls a specific function based on the user input.

func main() {
     for {
          fmt.Println("Input '1' to List All Buckets in your Object Storage.")
          fmt.Println("Input '2' to Create a new Bucket.")
          fmt.Println("Input '3' to Delete a Bucket.")
          fmt.Println("Input '4' to Upload a File/Object.")
          fmt.Println("Input '5' to List All Files/Objects inside a Bucket.")
          fmt.Println("Input '6' to Download a File/Object from a Bucket.")
          fmt.Println("Input '7' to Delete a File/Object from a Bucket.")
          fmt.Println("Input '8' to Delete All Files/Objects from a Bucket.")
          fmt.Print("Your Input: ")
          fmt.Scan(&input)
          switch input {
          case 1:
               log.Println(listAllBuckets())
          case 2:
               createBucket()
          case 3:
               fmt.Print("Enter the Name of the Bucket that you want to Delete: ")
               deleteBucket()
          case 4:
               uploadObject()
          case 5:
               log.Println(listObjects())
          case 6:
               getObject()
          case 7:
               deleteObject()
          case 8:
               fmt.Print("Enter the Name of the Bucket that you want to Empty: ")
               deleteAllObjects()
          default:
               log.Println("Invalid Input! Please try again.")
               continue
          }

          fmt.Print("Do you want to rerun the program? (y/n): ")
          fmt.Scan(&input2)
          loop = strings.ToLower(input2)
          if loop == "n" {
               fmt.Println("Closing the Program...")
               time.Sleep(2 * time.Second)
               break
          } else if loop == "y" {
               continue
          } else {
               log.Fatalln("Invalid Input! Closing the Program.")
               time.Sleep(2 * time.Second)
          }
     }
}

Build the Bucket Project

After you have populated the main.go file with the example code, save and close the file. Then, compile the code into a binary executable file.

$ go build

Run the executable file, then select which bucket operation you like to perform.

$ ./Bucket

Serving Static Files with Vultr Object Storage

You must do two important things to serve static files with Vultr Object Storage. First, you have to Grant Public Read Access to all your static files. And second, you need to set the correct Content Type for each file, or else your files will be downloaded when you visit your static files' URL.

In this example, you will create a sample HTML and CSS static files and host them with Vultr Object Storage.

  1. You will use the same project Bucket and the same executable file Bucket.exe to create a bucket and upload files because its settings (Canned ACL and Content-Type) are set to Grant public read access and to set the content type automatically.

     $ cd Bucket
  2. Create an HTML file named index.html.

     $ nano index.html
  3. Paste this sample HTML Code to your index.html file, then save and exit the file.

     <!DOCTYPE html>
     <html lang="en">
     <head>
         <link rel="stylesheet" href="css/style.css">
         <meta name="viewport" content="width=device-width, initial-scale=1.0">
         <title>This is a Sample Static File Hosted with Vultr Object Storage</title>
     </head>
     <body>
         <h1>This is a Heading 1</h1>
         <h2>This is a Heading 2</h2>
         <h3>This is a Heading 3</h3>
         <h4>This is a Heading 4</h4>
         <h5>This is a Heading 5</h5>
         <h6>This is a Heading 6</h6>
         <p>This is a Sample Static File Hosted with Vultr Object Storage.</p>
     </body>
     </html>
  4. Create a folder named css.

  5. Create a CSS file named style.css inside the css folder.

     $ nano css/style.css
  6. Paste this sample CSS Code to your style.css file, then save and exit the file.

     h1 {color: blue;}
     h2 {color: red;}
     h3 {color: green;}
     h4 {background-color: blue;}
     h5 {background-color: red;}
     h6 {background-color: green;}
  7. Run the binary executable file Bucket.exe.

     $ ./Bucket
  8. Create a New Bucket. Enter 2 to create a new Bucket, then name the bucket as my-static-hosting or any bucket name you prefer.

    Here is the sample execution:

     Input '1' to List All Buckets in your Object Storage.
     Input '2' to Create a new Bucket.
     Input '3' to Delete a Bucket.
     Input '4' to Upload a File/Object.
     Input '5' to List All Files/Objects inside a Bucket.
     Input '6' to Download a File/Object from a Bucket.
     Input '7' to Delete a File/Object from a Bucket.
     Input '8' to Delete All Files/Objects from a Bucket.
     Your Input: 2
     Bucket names are unique to their location and must meet the following criteria:
     Only lowercase and starts with a letter or number. No spaces.
     Bucket name may contain dashes
     Must be between 3 and 63 characters long.
     Enter your preferred name for the Bucket: my-static-hosting
     2022/07/29 17:31:45 Successfully created bucket "my-static-hosting"
     Do you want to rerun the program? (y/n): n
     Closing the Program...
  9. Upload your index.html file and style.css file. Run Bucket.exe again, then enter 4 to upload a file. Rerun the program when prompted because you have to upload two files.

    Here is the sample program execution:

     $ ./Bucket
    
     Input '1' to List All Buckets in your Object Storage.
     Input '2' to Create a new Bucket.
     Input '3' to Delete a Bucket.
     Input '4' to Upload a File/Object.
     Input '5' to List All Files/Objects inside a Bucket.
     Input '6' to Download a File/Object from a Bucket.
     Input '7' to Delete a File/Object from a Bucket.
     Input '8' to Delete All Files/Objects from a Bucket.
     Your Input: 4
     Enter the name of the bucket where you want to upload the File/Object: my-static-hosting
     Enter the Path or Directory where you want to upload the File/Object in the bucket: (e.g., assets/css/): /
     Enter the Path to the file that you want to upload (e.g., css/styles.css): index.html
     2022/07/29 17:36:03 Uploading Object: index.html
     2022/07/29 17:36:04 File "index.html" was Uploaded Successfully.
     Do you want to rerun the program? (y/n): y
     Input '1' to List All Buckets in your Object Storage.
     Input '2' to Create a new Bucket.
     Input '3' to Delete a Bucket.
     Input '4' to Upload a File/Object.
     Input '5' to List All Files/Objects inside a Bucket.
     Input '6' to Download a File/Object from a Bucket.
     Input '7' to Delete a File/Object from a Bucket.
     Input '8' to Delete All Files/Objects from a Bucket.
     Your Input: 4
     Enter the name of the bucket where you want to upload the File/Object: my-static-hosting
     Enter the Path or Directory where you want to upload the File/Object in the bucket: (e.g., assets/css/): css/
     Enter the Path to the file that you want to upload (e.g., css/styles.css): css/style.css
     2022/07/29 17:36:16 Uploading Object: css/style.css
     2022/07/29 17:36:17 File "css/style.css" was Uploaded Successfully.
     Do you want to rerun the program? (y/n): n
     Closing the Program...
  10. In your web browser, navigate to https://bucketname.ewr1.vultrobjects.com/filename. For example, https://my-static-hosting.ewr1.vultrobjects.com/index.html.

  11. Notice that your HTML static files are displayed and not downloaded when you visit the file's URL. The linked CSS stylesheet is applied as well.

  12. You may upload other media types such as images, videos, and so on to further test serving static files with Vultr Object Storage.

More Information

To learn more about Vultr Object Storage, GoVultr V2, Vultr API v2, and AWS SDK for Go, please see these resources: