Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding local field to function for handling transient spec (Local.Remote) #2121

Merged
merged 1 commit into from
Jan 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions cmd/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ EXAMPLES
"Git revision (branch) to be used when deploying via the Git repository ($FUNC_GIT_BRANCH)")
cmd.Flags().StringP("git-dir", "d", f.Build.Git.ContextDir,
"Directory in the Git repository containing the function (default is the root) ($FUNC_GIT_DIR)")
cmd.Flags().BoolP("remote", "R", f.Deploy.Remote,
cmd.Flags().BoolP("remote", "R", f.Local.Remote,
"Trigger a remote deployment. Default is to deploy and build from the local system ($FUNC_REMOTE)")
cmd.Flags().String("pvc-size", f.Build.PVCSize,
"When triggering a remote deployment, set a custom volume size to allocate for the build operation ($FUNC_PVC_SIZE)")
Expand Down Expand Up @@ -506,8 +506,8 @@ func (c deployConfig) Configure(f fn.Function) (fn.Function, error) {
f.Build.Git.ContextDir = c.GitDir
f.Build.Git.Revision = c.GitBranch // TODO: should match; perhaps "refSpec"
f.Deploy.Namespace = c.Namespace
f.Deploy.Remote = c.Remote
f.Deploy.ServiceAccountName = c.ServiceAccountName
f.Local.Remote = c.Remote

// PVCSize
// If a specific value is requested, ensure it parses as a resource.Quantity
Expand Down
6 changes: 3 additions & 3 deletions cmd/deploy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1433,7 +1433,7 @@ func TestDeploy_RemotePersists(t *testing.T) {
if f, err = fn.NewFunction(root); err != nil {
t.Fatal(err)
}
if !f.Deploy.Remote {
if !f.Local.Remote {
t.Fatalf("value of remote flag not persisted")
}

Expand All @@ -1449,7 +1449,7 @@ func TestDeploy_RemotePersists(t *testing.T) {
if f, err = fn.NewFunction(root); err != nil {
t.Fatal(err)
}
if !f.Deploy.Remote {
if !f.Local.Remote {
t.Fatalf("value of remote flag not persisted")
}

Expand All @@ -1464,7 +1464,7 @@ func TestDeploy_RemotePersists(t *testing.T) {
if f, err = fn.NewFunction(root); err != nil {
t.Fatal(err)
}
if f.Deploy.Remote {
if f.Local.Remote {
t.Fatalf("value of remote flag not persisted")
}
}
Expand Down
56 changes: 50 additions & 6 deletions pkg/functions/function.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,18 @@ const (

// RunDataDir holds transient runtime metadata
// By default it is excluded from source control.
RunDataDir = ".func"
RunDataDir = ".func"
RunDataLocalFile = "local.yaml"
)

// Local represents the transient runtime metadata which
// is only relevant to the local copy of the function
Comment on lines +29 to +30
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good explanation

type Local struct {
// Remote indicates the deployment (and possibly build) process are to
// be triggered in a remote environment rather than run locally.
Remote bool `yaml:"remote,omitempty"`
}

// Function
type Function struct {
// SpecVersion at which this function is known to be compatible.
Expand Down Expand Up @@ -87,6 +96,8 @@ type Function struct {

// Deploy defines the deployment properties for a function
Deploy DeploySpec `yaml:"deploy,omitempty"`

Local Local `yaml:"-"`
}

// BuildSpec
Expand Down Expand Up @@ -137,10 +148,6 @@ type DeploySpec struct {
// Namespace into which the function is deployed on supported platforms.
Namespace string `yaml:"namespace,omitempty"`

// Remote indicates the deployment (and possibly build) process are to
// be triggered in a remote environment rather than run locally.
Remote bool `yaml:"remote,omitempty"`

// Map containing user-supplied annotations
// Example: { "division": "finance" }
Annotations map[string]string `yaml:"annotations,omitempty"`
Expand Down Expand Up @@ -257,6 +264,7 @@ func NewFunction(root string) (f Function, err error) {
errorText += "\n" + "Migration: " + functionMigrationError.Error()
return Function{}, errors.New(errorText)
}
f.Local, err = f.newLocal()
return
}

Expand Down Expand Up @@ -385,7 +393,23 @@ func (f Function) Write() (err error) {
}
// TODO: open existing file for writing, such that existing permissions
// are preserved?
return os.WriteFile(filepath.Join(f.Root, FunctionFile), bb, 0644)
err = os.WriteFile(filepath.Join(f.Root, FunctionFile), bb, 0644)
if err != nil {
return
}

// Write local settings
err = ensureRunDataDir(f.Root)
if err != nil {
return
}
if bb, err = yaml.Marshal(&f.Local); err != nil {
return
}
localConfigPath := filepath.Join(f.Root, RunDataDir, RunDataLocalFile)

err = os.WriteFile(localConfigPath, bb, 0644)
return
}

type stampOptions struct{ journal bool }
Expand Down Expand Up @@ -666,3 +690,23 @@ func (f Function) BuildStamp() string {
}
return string(b)
}

// localSettings returns the local settings set for the function
func (f Function) newLocal() (localConfig Local, err error) {
err = ensureRunDataDir(f.Root)
if err != nil {
return
}
localSettingsPath := filepath.Join(f.Root, RunDataDir, RunDataLocalFile)
if _, err = os.Stat(localSettingsPath); os.IsNotExist(err) {
err = nil
return
}
b, err := os.ReadFile(localSettingsPath)
if err != nil {
return
}

err = yaml.Unmarshal(b, &localConfig)
return
}
149 changes: 149 additions & 0 deletions pkg/functions/function_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ import (
"testing"
"time"

"github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/config"
"github.com/go-git/go-git/v5/plumbing/object"
"github.com/google/go-cmp/cmp"

fn "knative.dev/func/pkg/functions"
Expand Down Expand Up @@ -398,3 +401,149 @@ func TestFunction_Stamp(t *testing.T) {
t.Fatal("expected journal log not found")
}
}

// TestFunction_Local checks if writing a function with custom Local spec
// stays the same for the current system. The test does the following:
//
// create a new function
// set Local.Remote to true
// write it to the disk
// load it again into a new function object
//
// The load should be successful and Local.Remote should be true
func TestFunction_Local(t *testing.T) {
root, rm := Mktemp(t)
defer rm()
fConfig := fn.Function{Root: root, Runtime: "go", Name: "f"}
client := fn.New(fn.WithBuilder(mock.NewBuilder()), fn.WithRegistry(TestRegistry))
f, err := client.Init(fConfig)
if err != nil {
t.Fatal(err)
}
f.Local.Remote = true

err = f.Write()
if err != nil {
t.Fatal(err)
}

// Load the function from the same location
f, err = fn.NewFunction(root)
if err != nil {
t.Fatal(err)
}

if !f.Local.Remote {
t.Fatal("expected remote flag to be set")
}
}

// TestFunction_LocalTransient ensures that the Local field is transient and
// is not serialised in a way that affects other clones of the function.
// The test does the following:
//
// create a function (with Local.Remote set)
// push the function to a remote repo (locally setup for the test)
// clone the function from the remote repo into a new location
//
// The new function should not have Local.Remote set (as it is a transient field)
func TestFunction_LocalTransient(t *testing.T) {

// Initialise a new function
root, rm := Mktemp(t)
defer rm()

fConfig := fn.Function{Root: root, Runtime: "go", Name: "f", Image: "test:latest"}
client := fn.New(fn.WithBuilder(mock.NewBuilder()))
f, err := client.Init(fConfig)
if err != nil {
t.Fatal(err)
}
f.Local.Remote = true

err = f.Write()
if err != nil {
t.Fatal(err)
}

// Initialise the function directory as a git repo
repo, err := git.PlainInit(root, false)
if err != nil {
t.Fatal(err)
}

// commit the function files
wt, err := repo.Worktree()
if err != nil {
t.Fatal(err)
}
if _, err = wt.Add("."); err != nil {
t.Fatal(err)
}
if _, err = wt.Commit("init", &git.CommitOptions{
All: true,
AllowEmptyCommits: false,
Author: &object.Signature{
Name: "xyz",
Email: "xyz@abc.com",
When: time.Now(),
},
Committer: &object.Signature{
Name: "xyz",
Email: "xyz@abc.com",
When: time.Now(),
},
}); err != nil {
t.Fatal(err)
}

// Create a remote and push the function
remotePath, remoteRm := Mktemp(t)
defer remoteRm()
if _, err = git.PlainInit(remotePath, true); err != nil {
t.Fatal(err)
}

_, err = repo.CreateRemote(&config.RemoteConfig{
Name: "origin",
URLs: []string{remotePath},
Mirror: false,
Fetch: nil,
})
if err != nil {
t.Fatal(err)
}

err = repo.Push(&git.PushOptions{
RemoteName: "origin",
RemoteURL: remotePath,
InsecureSkipTLS: true,
})
if err != nil {
t.Fatal()
}

// Create a new directory to clone the function in
newRoot, newRm := Mktemp(t)
defer newRm()

// Clone the pushed function
_, err = git.PlainClone(newRoot, false, &git.CloneOptions{
URL: remotePath,
RemoteName: "origin",
InsecureSkipTLS: true,
})
if err != nil {
t.Fatal(err)
}

// Read the function from the new location
newFunc, err := fn.NewFunction(newRoot)
if err != nil {
t.Fatal(newFunc, err)
}

if newFunc.Local.Remote {
t.Fatal("Remote not supposed to be set")
}
}
2 changes: 1 addition & 1 deletion pkg/pipelines/tekton/gitlab_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,9 @@ func TestGitlab(t *testing.T) {
PVCSize: "256Mi",
},
Deploy: fn.DeploySpec{
Remote: true,
Namespace: ns,
},
Local: fn.Local{Remote: true},
}
f = fn.NewFunctionWith(f)
err = f.Write()
Expand Down
4 changes: 0 additions & 4 deletions schema/func_yaml-schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,6 @@
"type": "string",
"description": "Namespace into which the function is deployed on supported platforms."
},
"remote": {
"type": "boolean",
"description": "Remote indicates the deployment (and possibly build) process are to\nbe triggered in a remote environment rather than run locally."
},
"annotations": {
"patternProperties": {
".*": {
Expand Down
Loading