Skip to content

Commit

Permalink
refactor: split add/del/set code into files
Browse files Browse the repository at this point in the history
  • Loading branch information
tri-adam committed Jun 18, 2024
1 parent f2f07f5 commit 84ca397
Show file tree
Hide file tree
Showing 8 changed files with 967 additions and 883 deletions.
81 changes: 81 additions & 0 deletions pkg/sif/add.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// Copyright (c) 2018-2023, Sylabs Inc. All rights reserved.
// Copyright (c) 2017, SingularityWare, LLC. All rights reserved.
// Copyright (c) 2017, Yannick Cote <yhcote@gmail.com> All rights reserved.
// This software is licensed under a 3-clause BSD license. Please consult the
// LICENSE file distributed with the sources of this project regarding your
// rights to use or distribute this software.

package sif

import (
"fmt"
"time"
)

// addOpts accumulates object add options.
type addOpts struct {
t time.Time
}

// AddOpt are used to specify object add options.
type AddOpt func(*addOpts) error

// OptAddDeterministic sets header/descriptor fields to values that support deterministic
// modification of images.
func OptAddDeterministic() AddOpt {
return func(ao *addOpts) error {
ao.t = time.Time{}
return nil
}
}

// OptAddWithTime specifies t as the image modification time.
func OptAddWithTime(t time.Time) AddOpt {
return func(ao *addOpts) error {
ao.t = t
return nil
}
}

// AddObject adds a new data object and its descriptor into the specified SIF file.
//
// By default, the image modification time is set to the current time for non-deterministic images,
// and unset otherwise. To override this, consider using OptAddDeterministic or OptAddWithTime.
func (f *FileImage) AddObject(di DescriptorInput, opts ...AddOpt) error {
ao := addOpts{}

if !f.isDeterministic() {
ao.t = time.Now()
}

for _, opt := range opts {
if err := opt(&ao); err != nil {
return fmt.Errorf("%w", err)
}
}

// Find an unused descriptor.
i := 0
for _, rd := range f.rds {
if !rd.Used {
break
}
i++
}

if err := f.writeDataObject(i, di, ao.t); err != nil {
return fmt.Errorf("%w", err)
}

if err := f.writeDescriptors(); err != nil {
return fmt.Errorf("%w", err)
}

f.h.ModifiedAt = ao.t.Unix()

if err := f.writeHeader(); err != nil {
return fmt.Errorf("%w", err)
}

return nil
}
155 changes: 155 additions & 0 deletions pkg/sif/add_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
// Copyright (c) 2018-2023, Sylabs Inc. All rights reserved.
// This software is licensed under a 3-clause BSD license. Please consult the
// LICENSE file distributed with the sources of this project regarding your
// rights to use or distribute this software.

package sif

import (
"errors"
"testing"
"time"

"github.com/sebdah/goldie/v2"
)

func TestAddObject(t *testing.T) {
tests := []struct {
name string
createOpts []CreateOpt
di DescriptorInput
opts []AddOpt
wantErr error
}{
{
name: "ErrInsufficientCapacity",
createOpts: []CreateOpt{
OptCreateDeterministic(),
OptCreateWithDescriptorCapacity(0),
},
di: getDescriptorInput(t, DataGeneric, []byte{0xfe, 0xed}),
wantErr: errInsufficientCapacity,
},
{
name: "ErrPrimaryPartition",
createOpts: []CreateOpt{
OptCreateDeterministic(),
OptCreateWithDescriptors(
getDescriptorInput(t, DataPartition, []byte{0xfa, 0xce},
OptPartitionMetadata(FsSquash, PartPrimSys, "386"),
),
),
},
di: getDescriptorInput(t, DataPartition, []byte{0xfe, 0xed},
OptPartitionMetadata(FsSquash, PartPrimSys, "amd64"),
),
wantErr: errPrimaryPartition,
},
{
name: "Deterministic",
createOpts: []CreateOpt{
OptCreateWithID("de170c43-36ab-44a8-bca9-1ea1a070a274"),
OptCreateWithTime(time.Unix(946702800, 0)),
},
di: getDescriptorInput(t, DataGeneric, []byte{0xfa, 0xce}),
opts: []AddOpt{
OptAddDeterministic(),
},
},
{
name: "WithTime",
createOpts: []CreateOpt{
OptCreateDeterministic(),
},
di: getDescriptorInput(t, DataGeneric, []byte{0xfa, 0xce}),
opts: []AddOpt{
OptAddWithTime(time.Unix(946702800, 0)),
},
},
{
name: "Empty",
createOpts: []CreateOpt{
OptCreateDeterministic(),
},
di: getDescriptorInput(t, DataGeneric, []byte{0xfa, 0xce}),
},
{
name: "EmptyNotAligned",
createOpts: []CreateOpt{
OptCreateDeterministic(),
},
di: getDescriptorInput(t, DataGeneric, []byte{0xfa, 0xce},
OptObjectAlignment(0),
),
},
{
name: "EmptyAligned",
createOpts: []CreateOpt{
OptCreateDeterministic(),
},
di: getDescriptorInput(t, DataGeneric, []byte{0xfa, 0xce},
OptObjectAlignment(128),
),
},
{
name: "NotEmpty",
createOpts: []CreateOpt{
OptCreateDeterministic(),
OptCreateWithDescriptors(
getDescriptorInput(t, DataGeneric, []byte{0xfa, 0xce}),
),
},
di: getDescriptorInput(t, DataPartition, []byte{0xfe, 0xed},
OptPartitionMetadata(FsSquash, PartPrimSys, "386"),
),
},
{
name: "NotEmptyNotAligned",
createOpts: []CreateOpt{
OptCreateDeterministic(),
OptCreateWithDescriptors(
getDescriptorInput(t, DataGeneric, []byte{0xfa, 0xce}),
),
},
di: getDescriptorInput(t, DataPartition, []byte{0xfe, 0xed},
OptPartitionMetadata(FsSquash, PartPrimSys, "386"),
OptObjectAlignment(0),
),
},
{
name: "NotEmptyAligned",
createOpts: []CreateOpt{
OptCreateDeterministic(),
OptCreateWithDescriptors(
getDescriptorInput(t, DataGeneric, []byte{0xfa, 0xce}),
),
},
di: getDescriptorInput(t, DataPartition, []byte{0xfe, 0xed},
OptPartitionMetadata(FsSquash, PartPrimSys, "386"),
OptObjectAlignment(128),
),
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var b Buffer

f, err := CreateContainer(&b, tt.createOpts...)
if err != nil {
t.Fatal(err)
}

if got, want := f.AddObject(tt.di, tt.opts...), tt.wantErr; !errors.Is(got, want) {
t.Errorf("got error %v, want %v", got, want)
}

if err := f.UnloadContainer(); err != nil {
t.Error(err)
}

g := goldie.New(t, goldie.WithTestNameForDir(true))
g.Assert(t, tt.name, b.Bytes())
})
}
}
Loading

0 comments on commit 84ca397

Please sign in to comment.