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

Feat: Adding limit field to template and Taks, overriding the CLI args from task. #2420

Open
wants to merge 14 commits into
base: develop
Choose a base branch
from
Open
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
113 changes: 62 additions & 51 deletions api-docs.yml

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions db/Migration.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ func GetMigrations() []Migration {
{Version: "2.10.16"},
{Version: "2.10.24"},
{Version: "2.10.26"},
{Version: "2.10.27"},
{Version: "2.10.28"},
{Version: "2.10.33"},
}
Expand Down
15 changes: 8 additions & 7 deletions db/Task.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ package db

import (
"fmt"
"github.com/go-gorp/gorp/v3"
"time"

"github.com/go-gorp/gorp/v3"
"github.com/semaphoreui/semaphore/pkg/task_logger"
"github.com/semaphoreui/semaphore/util"
)
Expand All @@ -22,12 +22,13 @@ type Task struct {
Diff bool `db:"diff" json:"diff"`

// override variables
Playbook string `db:"playbook" json:"playbook"`
Environment string `db:"environment" json:"environment"`
Limit string `db:"hosts_limit" json:"limit"`
Secret string `db:"-" json:"secret"`
Arguments *string `db:"arguments" json:"arguments"`
GitBranch *string `db:"git_branch" json:"git_branch"`
Playbook string `db:"playbook" json:"playbook"`
Environment string `db:"environment" json:"environment"`
Limit *string `db:"hosts_limit" json:"limit"`
Secret string `db:"-" json:"secret"`
Arguments *string `db:"arguments" json:"arguments"`
RemovedArguments []string `db:"-" json:"removed_arguments"`
GitBranch *string `db:"git_branch" json:"git_branch"`

UserID *int `db:"user_id" json:"user_id"`
IntegrationID *int `db:"integration_id" json:"integration_id"`
Expand Down
2 changes: 2 additions & 0 deletions db/Template.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@ type Template struct {

ViewID *int `db:"view_id" json:"view_id" backup:"-"`

Limit *string `db:"hosts_limit" json:"limit" backup:"-"`

LastTask *TaskWithTpl `db:"-" json:"last_task" backup:"-"`

Autorun bool `db:"autorun" json:"autorun"`
Expand Down
4 changes: 3 additions & 1 deletion db/sql/migration.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ package sql

import (
"fmt"
"github.com/go-gorp/gorp/v3"
"path"
"regexp"
"strings"
"time"

"github.com/go-gorp/gorp/v3"
"github.com/semaphoreui/semaphore/db"
log "github.com/sirupsen/logrus"
)
Expand Down Expand Up @@ -179,6 +179,8 @@ func (d *SqlDb) ApplyMigration(migration db.Migration) error {
err = migration_2_8_26{db: d}.PostApply(tx)
case "2.8.42":
err = migration_2_8_42{db: d}.PostApply(tx)
case "2.10.27":
err = migration_2_10_27{db: d}.PostApply(tx)
}

if err != nil {
Expand Down
19 changes: 19 additions & 0 deletions db/sql/migration_2_10_27.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package sql

import "github.com/go-gorp/gorp/v3"

type migration_2_10_27 struct {
db *SqlDb
}

func (m migration_2_10_27) PostApply(tx *gorp.Transaction) error {
switch m.db.sql.Dialect.(type) {
case gorp.MySQLDialect:
_, _ = tx.Exec(m.db.PrepareQuery("alter table `task` modify `hosts_limit` text default null;"))
case gorp.PostgresDialect:
_, err := tx.Exec(
m.db.PrepareQuery("alter table `task` alter column `hosts_limit` type text, alter column `hosts_limit` drop not null, alter column `hosts_limit` set default null;"))
return err
}
return nil
}
1 change: 1 addition & 0 deletions db/sql/migrations/v2.10.27.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
alter table project__template add column hosts_limit text default null;
8 changes: 6 additions & 2 deletions db/sql/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ func (d *SqlDb) CreateTemplate(template db.Template) (newTemplate db.Template, e
"id",
"insert into project__template (project_id, inventory_id, repository_id, environment_id, "+
"name, playbook, arguments, allow_override_args_in_task, description, `type`, start_version,"+
"build_template_id, view_id, autorun, survey_vars, suppress_success_alerts, app, git_branch)"+
"values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
"build_template_id, view_id, hosts_limit, autorun, survey_vars, suppress_success_alerts, app, git_branch)"+
"values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
template.ProjectID,
template.InventoryID,
template.RepositoryID,
Expand All @@ -34,6 +34,7 @@ func (d *SqlDb) CreateTemplate(template db.Template) (newTemplate db.Template, e
template.StartVersion,
template.BuildTemplateID,
template.ViewID,
template.Limit,
template.Autorun,
db.ObjectToJSON(template.SurveyVars),
template.SuppressSuccessAlerts,
Expand Down Expand Up @@ -81,6 +82,7 @@ func (d *SqlDb) UpdateTemplate(template db.Template) error {
"start_version=?,"+
"build_template_id=?, "+
"view_id=?, "+
"hosts_limit=?, "+
"autorun=?, "+
"survey_vars=?, "+
"suppress_success_alerts=?, "+
Expand All @@ -99,6 +101,7 @@ func (d *SqlDb) UpdateTemplate(template db.Template) error {
template.StartVersion,
template.BuildTemplateID,
template.ViewID,
template.Limit,
template.Autorun,
db.ObjectToJSON(template.SurveyVars),
template.SuppressSuccessAlerts,
Expand Down Expand Up @@ -138,6 +141,7 @@ func (d *SqlDb) GetTemplates(projectID int, filter db.TemplateFilter, params db.
"pt.build_template_id",
"pt.start_version",
"pt.view_id",
"pt.hosts_limit",
"pt.`app`",
"pt.`git_branch`",
"pt.survey_vars",
Expand Down
7 changes: 7 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@ require (
golang.org/x/oauth2 v0.17.0
)

require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

require (
dario.cat/mergo v1.0.0 // indirect
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect
Expand Down Expand Up @@ -55,6 +61,7 @@ require (
github.com/sergi/go-diff v1.3.1 // indirect
github.com/skeema/knownhosts v1.2.1 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/stretchr/testify v1.9.0
github.com/xanzy/ssh-agent v0.3.3 // indirect
golang.org/x/mod v0.15.0 // indirect
golang.org/x/net v0.23.0 // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,8 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/thedevsaddam/gojsonq/v2 v2.5.2 h1:CoMVaYyKFsVj6TjU6APqAhAvC07hTI6IQen8PHzHYY0=
github.com/thedevsaddam/gojsonq/v2 v2.5.2/go.mod h1:bv6Xa7kWy82uT0LnXPE2SzGqTj33TAEeR560MdJkiXs=
github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
Expand Down
67 changes: 63 additions & 4 deletions services/tasks/LocalJob.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@ package tasks

import (
"encoding/json"
"errors"
"fmt"
"maps"
"os"
"regexp"
"slices"

"path"
"strconv"
Expand Down Expand Up @@ -378,14 +381,43 @@ func (t *LocalJob) getPlaybookArgs(username string, incomingVersion *string) (ar
}
}

if t.Task.Limit != "" {
t.Log("--limit=" + t.Task.Limit)
taskExtraArgs = append(taskExtraArgs, "--limit="+t.Task.Limit)
if t.Task.Limit != nil && len(*t.Task.Limit) > 0 {
t.Log("--limit=" + *t.Task.Limit)
taskExtraArgs = append(taskExtraArgs, "--limit="+*t.Task.Limit)
}

for _, ra := range t.Task.RemovedArguments {
rai := slices.Index(templateExtraArgs, ra)
if rai != -1 {
templateExtraArgs = append(templateExtraArgs[:rai], templateExtraArgs[rai+1:]...)
}
}

for _, taskArg := range taskExtraArgs {
for tai, tmplArg := range templateExtraArgs {
ok, err := isCLIArgsOverridden(tmplArg, taskArg)
if err != nil {
t.Log(err.Error())
}

if ok {
templateExtraArgs = append(templateExtraArgs[:tai], templateExtraArgs[tai+1:]...)
break
}

if slices.Contains(templateExtraArgs, taskArg) {
templateExtraArgs = append(templateExtraArgs[:tai], templateExtraArgs[tai+1:]...)
}

}
}

templateExtraArgs = append(templateExtraArgs, taskExtraArgs...)

args = append(args, templateExtraArgs...)
args = append(args, taskExtraArgs...)
// args = append(args, taskExtraArgs...) // old implementation
args = append(args, playbookName)
fmt.Println(args)

if line, ok := inputMap[db.AccessKeyRoleAnsibleUser]; ok {
inputs["SSH password:"] = line
Expand Down Expand Up @@ -615,3 +647,30 @@ func (t *LocalJob) installVaultKeyFiles() (err error) {

return
}

var errCliOverrideParseError = errors.New("the argument does not seem to be in required format")

// ? not sure where this is belong since there are no helpers in services.
func isCLIArgsOverridden(tmplArg string, taskArg string) (bool, error) {
tmplArgKeyReg, err := regexp.Compile(`=|\s`)
if err != nil {
return false, err
}
tmplArgKey := tmplArgKeyReg.Split(tmplArg, 2)

taskArgKeyReg, err := regexp.Compile(`=|\s`)
if err != nil {
return false, err
}
taskArgKey := taskArgKeyReg.Split(taskArg, 2)

if len(tmplArgKey) < 2 || len(taskArgKey) < 2 {
return false, errCliOverrideParseError
}

if tmplArgKey[0] == taskArgKey[0] && tmplArgKey[1] != taskArgKey[1] {
return true, nil
}

return false, nil
}
56 changes: 56 additions & 0 deletions services/tasks/LocalJob_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package tasks

import (
"testing"
)

func TestIsCLIArgsOverridden(t *testing.T) {
var args []struct {
tmplArg string
taskArg string
want bool
err error
} = []struct {
tmplArg string
taskArg string
want bool
err error
}{
{
tmplArg: "--ssh-extra-args=\"-p 3222\"",
taskArg: "--ssh-extra-args \"-p 3222\"",
Copy link
Collaborator

Choose a reason for hiding this comment

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

Single argument can not contain space which separates key and value. It must be 2 separate arguments. Otherwise ansible-playbook doesn't accept this argument (tested).

Copy link
Author

@Gnyblast Gnyblast Oct 29, 2024

Choose a reason for hiding this comment

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

I didn't get full what you mean? Do you mean that --ssh-extra-args will only work if separator is = sign? Like: -ssh-extra-args="-p 3222"? and won't work with --ssh-extra-args "-p 3222"? If that's the case, how do you prevent this on template CLI args declaration already? And this is not a user mistake if that's the case? On the other hand this piece of code only is to determine if they are passing same argument from task CLI args, so that it can be overriden, I don't really touch CLI args how they look like, regex is just checking either first = or first white space and splits it from there, so it can compare the passed option, --ssh-extra-args in this case.

Copy link
Collaborator

@fiftin fiftin Oct 30, 2024

Choose a reason for hiding this comment

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

@Gnyblast yes, it is true. Argument --ssh-extra-args "-p 3222" doesn't work in Semaphore.

You can try to call ansible CLI with params ansible-playbook '--ssh-extra-args "-p 3222"' and you will got error. But following command will work: ansible-playbook '--ssh-extra-args' '-p 3222'

Semaphore runs ansible by command like exec.Command("ansible-playbook", []string{"--ssh-extra-args \"-p 3222\""}) so it will not work. But following code will work: exec.Command("ansible-playbook", []string{"--ssh-extra-args", "-p 3222"})

Copy link
Author

@Gnyblast Gnyblast Nov 1, 2024

Choose a reason for hiding this comment

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

as I said before this piece of code here is just to test if the parser regex can split argument and it's value correctly by delimiter which is either = sign or a space. I don't do any formatting as you can see in the code below:

	for _, taskArg := range taskExtraArgs {
		for tai, tmplArg := range templateExtraArgs {
			ok, err := isCLIArgsOverridden(tmplArg, taskArg)
			if err != nil {
				t.Log(err.Error())
			}

			if ok {
				templateExtraArgs = append(templateExtraArgs[:tai], templateExtraArgs[tai+1:]...)
				break
			}

			if slices.Contains(templateExtraArgs, taskArg) {
				templateExtraArgs = append(templateExtraArgs[:tai], templateExtraArgs[tai+1:]...)
			}

		}
	}

first piece with isCLIArgsOverridden is the logic split them by delimiter and decide if it's same argument key with different value, the:
1- if yes, that means it's an override and delete the one on templateExtraArgs since taskExtraArgs will override that specific arguments
2- If not and if there are duplicate, then again delete the on in templateExtraArgs since taskExtraArgs already have a duplicate of it.

So if user does a mistake on how to pass a CLI argument, which they can do it now with the current code, it's a user mistake and should be fixed by passing it correctly but using = sign or escaping correctly. Nothing changes there.

want: false,
err: nil,
},
{
tmplArg: "--ssh-extra-args=\"-p 3222\"",
taskArg: "--ssh-extra-args \"-p 3223\"",
want: true,
err: nil,
},
{
tmplArg: "--ssh-extra-args=\"-p 3222\"",
taskArg: "--ssh-extra-args=\"-p 3223\"",
want: true,
err: nil,
},
{
tmplArg: "--ssh-extra-args=\"-p 3222\"",
taskArg: "--ssh-extra-args",
want: false,
err: errCliOverrideParseError,
},
}

for _, tc := range args {

got, err := isCLIArgsOverridden(tc.tmplArg, tc.taskArg)
if err != tc.err {
t.Fail()
}

if got != tc.want {
t.Fail()
}
}
}
2 changes: 2 additions & 0 deletions web/src/components/ArgsPicker.vue
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ export default {
watch: {
vars(val) {
this.var = val || [];
this.modifiedVars = (this.var || []).map((v) => ({ name: v }));
},
},

Expand Down Expand Up @@ -155,6 +156,7 @@ export default {
},

deleteVar(index) {
this.$emit('removed', this.modifiedVars[index].name);
this.modifiedVars.splice(index, 1);
this.$emit('change', this.modifiedVars.map((x) => x.name));
},
Expand Down
Loading
Loading