Skip to content
This repository has been archived by the owner on Jul 16, 2020. It is now read-only.

Commit

Permalink
Merge pull request #467 from 01org/sameo/topic/storage
Browse files Browse the repository at this point in the history
ciao-image: Separate all storage interfaces and implement posix fs data storage
  • Loading branch information
kaccardi authored Aug 16, 2016
2 parents af0cae8 + 381868b commit 005b6d1
Show file tree
Hide file tree
Showing 10 changed files with 437 additions and 234 deletions.
69 changes: 54 additions & 15 deletions ciao-image/service/cache.go → ciao-image/datastore/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,29 +12,33 @@
// See the License for the specific language governing permissions and
// limitations under the License.

package service
package datastore

import (
"io"
"sync"
)

type imageCache struct {
// ImageCache is an image metadata cache.
type ImageCache struct {
images map[string]Image
lock *sync.RWMutex
ds Datastore
metaDs MetaDataStore
rawDs RawDataStore
}

// Init initializes the datastore struct and must be called before anything.
func (c *imageCache) init(ds Datastore) error {
func (c *ImageCache) Init(rawDs RawDataStore, metaDs MetaDataStore) error {
c.images = make(map[string]Image)
c.lock = &sync.RWMutex{}
c.ds = ds
c.metaDs = metaDs
c.rawDs = rawDs

return nil
}

// Create will add an image to the datastore.
func (c *imageCache) createImage(i Image) error {
// CreateImage will add an image to the datastore.
func (c *ImageCache) CreateImage(i Image) error {
defer c.lock.Unlock()
c.lock.Lock()

Expand All @@ -43,8 +47,8 @@ func (c *imageCache) createImage(i Image) error {
return nil
}

// RetrieveAll gets returns all the known images.
func (c *imageCache) getAllImages() ([]Image, error) {
// GetAllImages gets returns all the known images.
func (c *ImageCache) GetAllImages() ([]Image, error) {
var images []Image

defer c.lock.RUnlock()
Expand All @@ -57,8 +61,8 @@ func (c *imageCache) getAllImages() ([]Image, error) {
return images, nil
}

// Retrieve returns the image specified by the ID string.
func (c *imageCache) getImage(ID string) (Image, error) {
// GetImage returns the image specified by the ID string.
func (c *ImageCache) GetImage(ID string) (Image, error) {
defer c.lock.RUnlock()
c.lock.RLock()

Expand All @@ -71,8 +75,8 @@ func (c *imageCache) getImage(ID string) (Image, error) {
return i, nil
}

// Update will modify an existing image.
func (c *imageCache) updateImage(i Image) error {
// UpdateImage will modify an existing image.
func (c *ImageCache) UpdateImage(i Image) error {
defer c.lock.Unlock()
c.lock.Lock()

Expand All @@ -88,8 +92,8 @@ func (c *imageCache) updateImage(i Image) error {
return nil
}

// Delete will delete an existing image.
func (c *imageCache) deleteImage(ID string) error {
// DeleteImage will delete an existing image.
func (c *ImageCache) DeleteImage(ID string) error {
defer c.lock.Unlock()
c.lock.Lock()

Expand All @@ -104,3 +108,38 @@ func (c *imageCache) deleteImage(ID string) error {

return nil
}

// UploadImage will read an image, save it and update the image cache.
func (c *ImageCache) UploadImage(ID string, body io.Reader) error {
c.lock.Lock()

image, ok := c.images[ID]
if !ok {
c.lock.Unlock()
return ErrNoImage
}

if image.State == Saving {
c.lock.Unlock()
return ErrImageSaving
}

image.State = Saving

c.lock.Unlock()

if c.rawDs != nil {
_, err := c.rawDs.Write(ID, body)
if err != nil {
return err
}
}

c.lock.Lock()

image.State = Active

c.lock.Unlock()

return nil
}
117 changes: 117 additions & 0 deletions ciao-image/datastore/datastore.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
// Copyright (c) 2016 Intel Corporation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package datastore

import (
"errors"
"io"
"time"

"github.com/01org/ciao/openstack/image"
)

// State represents the state of the image.
type State string

const (
// Created means that an empty image has been created
Created State = "created"

// Saving means the image is being saved
Saving State = "saving"

// Active means that the image is created, uploaded and ready to use.
Active State = "active"
)

// Status translate an image state to an openstack image status.
func (state State) Status() image.Status {
switch state {
case Created:
return image.Queued
case Saving:
return image.Saving
case Active:
return image.Active
}

return image.Active
}

// Visibility returns the image visibility
func (i Image) Visibility() image.Visibility {
if i.TenantID == "" {
return image.Public
}
return image.Private
}

// Type represents the valid image types.
type Type string

const (
// Raw is the raw image format.
Raw Type = "raw"

// QCow is the qcow2 format.
QCow Type = "qcow2"

// ISO is the iso format.
ISO Type = "iso"
)

// Image contains the information that ciao will store about the image
type Image struct {
ID string
State State
TenantID string
Name string
CreateTime time.Time
Type Type
}

var (
// ErrNoImage is returned when an image is not found.
ErrNoImage = errors.New("Image not found")

// ErrImageSaving is returned when an image is being uploaded.
ErrImageSaving = errors.New("Image being uploaded")
)

// DataStore is the image data storage interface.
type DataStore interface {
Init(RawDataStore, MetaDataStore) error
CreateImage(Image) error
GetAllImages() ([]Image, error)
GetImage(string) (Image, error)
UpdateImage(Image) error
DeleteImage(string) error
UploadImage(string, io.Reader) error
}

// MetaDataStore is the metadata storing interface that's used by
// image cache implementation.
type MetaDataStore interface {
Write(Image) error
Delete(ID string) error
GetAll() ([]Image, error)
}

// RawDataStore is the raw data storage interface that's used by the
// image cache implementation.
type RawDataStore interface {
Write(ID string, body io.Reader) (int64, error)
Delete(ID string) error
}
144 changes: 144 additions & 0 deletions ciao-image/datastore/datastore_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
// Copyright (c) 2016 Intel Corporation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package datastore

import (
"strings"
"testing"
)

func testCreateAndGet(t *testing.T, d RawDataStore, m MetaDataStore) {
i := Image{
ID: "validID",
State: Created,
}

cache := ImageCache{}
cache.Init(d, m)

// create the entry
err := cache.CreateImage(i)
if err != nil {
t.Fatal(err)
}

// retrieve the entry
image, err := cache.GetImage(i.ID)
if err != nil {
t.Fatal(err)
}

if image.ID != i.ID {
t.Fatal(err)
}
}

func testGetAll(t *testing.T, d RawDataStore, m MetaDataStore) {
i := Image{
ID: "validID",
State: Created,
}

cache := ImageCache{}
cache.Init(d, m)

// create the entry
err := cache.CreateImage(i)
if err != nil {
t.Fatal(err)
}

// retrieve the entry
images, err := cache.GetAllImages()
if err != nil {
t.Fatal(err)
}

if len(images) != 1 {
t.Fatalf("len is actually %d\n", len(images))
}

if images[0].ID != i.ID {
t.Fatal(err)
}
}

func testDelete(t *testing.T, d RawDataStore, m MetaDataStore) {
i := Image{
ID: "validID",
State: Created,
}

cache := ImageCache{}
cache.Init(d, m)

// create the entry
err := cache.CreateImage(i)
if err != nil {
t.Fatal(err)
}

// delete the entry
err = cache.DeleteImage(i.ID)
if err != nil {
t.Fatal(err)
}

// now attempt to retrive the entry
_, err = cache.GetImage(i.ID)
if err == nil {
t.Fatal(err)
}
}

func testUpload(t *testing.T, d RawDataStore, m MetaDataStore) {
i := Image{
ID: "validID",
State: Created,
}

cache := ImageCache{}
cache.Init(d, m)

// create the entry
err := cache.CreateImage(i)
if err != nil {
t.Fatal(err)
}

// Upload a string
err = cache.UploadImage(i.ID, strings.NewReader("Upload file"))
if err != nil {
t.Fatal(err)
}
}

var mountPoint = "/var/lib/ciao/images"

func TestPosixNoopCreateAndGet(t *testing.T) {
testCreateAndGet(t, &Posix{MountPoint: mountPoint}, &Noop{})
}

func TestPosixNoopGetAll(t *testing.T) {
testGetAll(t, &Posix{MountPoint: mountPoint}, &Noop{})
}

func TestPosixNoopDelete(t *testing.T) {
testDelete(t, &Posix{MountPoint: mountPoint}, &Noop{})
}

func TestPosixNoopUpload(t *testing.T) {
testUpload(t, &Posix{MountPoint: mountPoint}, &Noop{})
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.

package service
package datastore

// Noop is a Datastore implementation that does nothing.
// Use it only for development and testing purposes, data
Expand Down
Loading

0 comments on commit 005b6d1

Please sign in to comment.