common/ImageType: move to the store package

This is now only used for marshalling in the store, so keep the logic
where it is used.

Signed-off-by: Tom Gundersen <teg@jklm.no>
This commit is contained in:
Tom Gundersen 2020-05-11 23:23:30 +02:00
parent 1fd3b9d543
commit 2c3790f20c
4 changed files with 116 additions and 146 deletions

View file

@ -17,6 +17,26 @@ const (
IBFailed
)
// CustomJsonConversionError is thrown when parsing strings into enumerations
type CustomJsonConversionError struct {
reason string
}
// Error returns the error as a string
func (err *CustomJsonConversionError) Error() string {
return err.reason
}
// CustomTypeError is thrown when parsing strings into enumerations
type CustomTypeError struct {
reason string
}
// Error returns the error as a string
func (err *CustomTypeError) Error() string {
return err.reason
}
// ToString converts ImageBuildState into a human readable string
func (ibs ImageBuildState) ToString() string {
return getStateMapping()[int(ibs)]

View file

@ -1,141 +0,0 @@
package common
import (
"encoding/json"
"fmt"
)
// CustomJsonConversionError is thrown when parsing strings into enumerations
type CustomJsonConversionError struct {
reason string
}
// Error returns the error as a string
func (err *CustomJsonConversionError) Error() string {
return err.reason
}
// CustomTypeError is thrown when parsing strings into enumerations
type CustomTypeError struct {
reason string
}
// Error returns the error as a string
func (err *CustomTypeError) Error() string {
return err.reason
}
// Since Go has no generics, this is the only way to write common code for all the types present in this package.
// It uses weakly typed maps to convert between strings and integers which are then wrapped into type aliases.
// Specific implementations are bellow each type.
func unmarshalHelper(data []byte, jsonError, typeConversionError string, mapping map[string]int) (int, error) {
var stringInput string
err := json.Unmarshal(data, &stringInput)
if err != nil {
return 0, &CustomJsonConversionError{string(data) + jsonError}
}
value, exists := mapping[stringInput]
if !exists {
return 0, &CustomJsonConversionError{stringInput + typeConversionError}
}
return value, nil
}
// See unmarshalHelper for explanation
func marshalHelper(input int, mapping map[string]int, errorMessage string) ([]byte, error) {
for k, v := range mapping {
if v == input {
return json.Marshal(k)
}
}
return nil, &CustomJsonConversionError{fmt.Sprintf("%d %s", input, errorMessage)}
}
// See unmarshalHelper for introduction. Converts between TypeAlias(int) and string
func toStringHelper(mapping map[string]int, tag int) (string, bool) {
for k, v := range mapping {
if v == tag {
return k, true
}
}
return "", false
}
type ImageType int
// NOTE: If you want to add more constants here, don't forget to add a mapping below
const (
Azure ImageType = iota
Aws
LiveISO
OpenStack
Qcow2Generic
Vmware
RawFilesystem
PartitionedDisk
TarArchive
)
// getArchMapping is a helper function that defines the conversion from JSON string value
// to ImageType.
func getImageTypeMapping() map[string]int {
mapping := map[string]int{
"Azure": int(Azure),
"AWS": int(Aws),
"LiveISO": int(LiveISO),
"OpenStack": int(OpenStack),
"qcow2": int(Qcow2Generic),
"VMWare": int(Vmware),
"Raw-filesystem": int(RawFilesystem),
"Partitioned-disk": int(PartitionedDisk),
"Tar": int(TarArchive),
}
return mapping
}
// TODO: check the mapping here:
func getCompatImageTypeMapping() map[int]string {
mapping := map[int]string{
int(Azure): "vhd",
int(Aws): "ami",
int(LiveISO): "liveiso",
int(OpenStack): "openstack",
int(Qcow2Generic): "qcow2",
int(Vmware): "vmdk",
int(RawFilesystem): "ext4-filesystem",
int(PartitionedDisk): "partitioned-disk",
int(TarArchive): "tar",
}
return mapping
}
func (imgType *ImageType) UnmarshalJSON(data []byte) error {
value, err := unmarshalHelper(data, " is not a valid JSON value", " is not a valid image type", getImageTypeMapping())
if err != nil {
return err
}
*imgType = ImageType(value)
return nil
}
func (imgType ImageType) MarshalJSON() ([]byte, error) {
return marshalHelper(int(imgType), getImageTypeMapping(), "is not a valid image type tag")
}
func (imgType ImageType) ToCompatString() (string, bool) {
str, exists := getCompatImageTypeMapping()[int(imgType)]
return str, exists
}
func ImageTypeFromCompatString(input string) (ImageType, bool) {
for k, v := range getCompatImageTypeMapping() {
if v == input {
return ImageType(k), true
}
}
return ImageType(999), false
}
func (imgType ImageType) ToString() (string, bool) {
return toStringHelper(getImageTypeMapping(), int(imgType))
}

View file

@ -1,6 +1,9 @@
package store
import (
"encoding/json"
"errors"
"fmt"
"sort"
"time"
@ -38,7 +41,7 @@ type composesV0 map[uuid.UUID]composeV0
// ImageBuild represents a single image build inside a compose
type imageBuildV0 struct {
ID int `json:"id"`
ImageType common.ImageType `json:"image_type"`
ImageType imageType `json:"image_type"`
Manifest *osbuild.Manifest `json:"manifest"`
Targets []*target.Target `json:"targets"`
JobCreated time.Time `json:"job_created"`
@ -99,7 +102,7 @@ func newComposesFromV0(composesStruct composesV0, arch distro.Arch) map[uuid.UUI
Blueprint: composeStruct.Blueprint,
}
for _, imgBuild := range composeStruct.ImageBuilds {
imgTypeString, valid := imgBuild.ImageType.ToCompatString()
imgTypeString, valid := imgBuild.ImageType.toCompatString()
if !valid {
// Invalid type strings in serialization format, this may happen
// on upgrades. Ignore the image build.
@ -251,7 +254,7 @@ func newComposesV0(composes map[uuid.UUID]Compose) composesV0 {
panic("the was a compose with zero image builds, that is forbidden")
}
for _, imgBuild := range compose.ImageBuilds {
imgType, valid := common.ImageTypeFromCompatString(imgBuild.ImageType.Name())
imgType, valid := imageTypeFromCompatString(imgBuild.ImageType.Name())
if !valid {
panic("invalid image type")
}
@ -317,3 +320,91 @@ func (store *Store) toStoreV0() *storeV0 {
Commits: newCommitsV0(store.blueprintsCommits),
}
}
type imageType int
// NOTE: If you want to add more constants here, don't forget to add a mapping below
const (
Azure imageType = iota
Aws
LiveISO
OpenStack
Qcow2Generic
Vmware
RawFilesystem
PartitionedDisk
TarArchive
)
// getArchMapping is a helper function that defines the conversion from JSON string value
// to ImageType.
func getImageTypeMapping() map[string]int {
mapping := map[string]int{
"Azure": int(Azure),
"AWS": int(Aws),
"LiveISO": int(LiveISO),
"OpenStack": int(OpenStack),
"qcow2": int(Qcow2Generic),
"VMWare": int(Vmware),
"Raw-filesystem": int(RawFilesystem),
"Partitioned-disk": int(PartitionedDisk),
"Tar": int(TarArchive),
}
return mapping
}
// TODO: check the mapping here:
func getCompatImageTypeMapping() map[int]string {
mapping := map[int]string{
int(Azure): "vhd",
int(Aws): "ami",
int(LiveISO): "liveiso",
int(OpenStack): "openstack",
int(Qcow2Generic): "qcow2",
int(Vmware): "vmdk",
int(RawFilesystem): "ext4-filesystem",
int(PartitionedDisk): "partitioned-disk",
int(TarArchive): "tar",
}
return mapping
}
func (imgType imageType) toCompatString() (string, bool) {
str, exists := getCompatImageTypeMapping()[int(imgType)]
return str, exists
}
func imageTypeFromCompatString(input string) (imageType, bool) {
for k, v := range getCompatImageTypeMapping() {
if v == input {
return imageType(k), true
}
}
return imageType(999), false
}
func (imgType *imageType) UnmarshalJSON(data []byte) error {
mapping := getImageTypeMapping()
var stringInput string
err := json.Unmarshal(data, &stringInput)
if err != nil {
return err
}
value, exists := mapping[stringInput]
if !exists {
return errors.New(stringInput + " is not a valid image type")
}
*imgType = imageType(value)
return nil
}
func (imgType imageType) MarshalJSON() ([]byte, error) {
mapping := getImageTypeMapping()
input := int(imgType)
for k, v := range mapping {
if v == input {
return json.Marshal(k)
}
}
return nil, fmt.Errorf("%d is not a valid image type tag", input)
}

View file

@ -1,4 +1,4 @@
package common
package store
import (
"encoding/json"
@ -7,7 +7,7 @@ import (
func TestImageType_UnmarshalJSON(t *testing.T) {
dict := struct {
ImageTypes []ImageType `json:"image_types"`
ImageTypes []imageType `json:"image_types"`
}{}
input := `{"image_types":["qcow2", "Azure"]}`
err := json.Unmarshal([]byte(input), &dict)