From 331ba9bb37045453c170cf013f740434f6cd41a7 Mon Sep 17 00:00:00 2001 From: Rain Date: Wed, 29 Apr 2020 20:50:15 +0800 Subject: [PATCH 1/4] feat/ansible_pb: Add support for requirements You can declare the source info of the referenced role in requirements. --- .../models/ansibleplaybooks_v2.go | 14 ++-- pkg/util/ansiblev2/requirements.go | 70 +++++++++++++++++++ pkg/util/ansiblev2/requirements_test.go | 45 ++++++++++++ pkg/util/ansiblev2/session.go | 48 +++++++++++-- 4 files changed, 167 insertions(+), 10 deletions(-) create mode 100644 pkg/util/ansiblev2/requirements.go create mode 100644 pkg/util/ansiblev2/requirements_test.go diff --git a/pkg/ansibleserver/models/ansibleplaybooks_v2.go b/pkg/ansibleserver/models/ansibleplaybooks_v2.go index 75fb44f0d8..fdfd0053b3 100644 --- a/pkg/ansibleserver/models/ansibleplaybooks_v2.go +++ b/pkg/ansibleserver/models/ansibleplaybooks_v2.go @@ -39,12 +39,13 @@ import ( type SAnsiblePlaybookV2 struct { db.SVirtualResourceBase - Playbook string `length:"text" nullable:"false" create:"required" get:"user"` - Inventory string `length:"text" nullable:"false" create:"required" get:"user"` - Files string `length:"text" nullable:"false" create:"optional" get:"user"` - Output string `length:"medium" get:"user"` - StartTime time.Time `list:"user"` - EndTime time.Time `list:"user"` + Playbook string `length:"text" nullable:"false" create:"required" get:"user"` + Inventory string `length:"text" nullable:"false" create:"required" get:"user"` + Requirements string `length:"test" nullable:"false" create:"required" get:"user"` + Files string `length:"text" nullable:"false" create:"optional" get:"user"` + Output string `length:"medium" get:"user"` + StartTime time.Time `list:"user"` + EndTime time.Time `list:"user"` CreatorMark string `length:"32" nullable:"false" create:"optional" get:"user"` } @@ -196,6 +197,7 @@ func (apb *SAnsiblePlaybookV2) runPlaybook(ctx context.Context, userCred mcclien Inventory(apb.Inventory). Playbook(apb.Playbook). PrivateKey(privateKey). + Requirements(apb.Requirements). Files(files). OutputWriter(&ansiblePlaybookOutputWriter{apb}) man.sessions.Add(apb.Id, sess) diff --git a/pkg/util/ansiblev2/requirements.go b/pkg/util/ansiblev2/requirements.go new file mode 100644 index 0000000000..5a9398316d --- /dev/null +++ b/pkg/util/ansiblev2/requirements.go @@ -0,0 +1,70 @@ +// Copyright 2019 Yunion +// +// 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 ansiblev2 + +import "gopkg.in/yaml.v2" + +type RoleSource struct { + Name string + Src string + Version string +} + +func (rs *RoleSource) MarshalYAML() (interface{}, error) { + r := map[string]string{ + "src": rs.Src, + } + if len(rs.Name) > 0 { + r["name"] = rs.Name + } + if len(rs.Version) > 0 { + r["version"] = rs.Version + } + return r, nil +} + +type Requirements struct { + RoleSources []RoleSource +} + +func NewRequirements(rss ...RoleSource) *Requirements { + return &Requirements{ + RoleSources: rss, + } +} + +func (rm *Requirements) AddRoleSource(rss ...RoleSource) { + rm.RoleSources = append(rm.RoleSources, rss...) +} + +func (rm *Requirements) MarshalYAML() (interface{}, error) { + var err error + r := make([]interface{}, len(rm.RoleSources)) + for i := range rm.RoleSources { + r[i], err = rm.RoleSources[i].MarshalYAML() + if err != nil { + return nil, err + } + } + return r, nil +} + +func (rm *Requirements) String() string { + r, err := yaml.Marshal(rm) + if err != nil { + panic(err) + } + return string(r) +} diff --git a/pkg/util/ansiblev2/requirements_test.go b/pkg/util/ansiblev2/requirements_test.go new file mode 100644 index 0000000000..33db1f3896 --- /dev/null +++ b/pkg/util/ansiblev2/requirements_test.go @@ -0,0 +1,45 @@ +// Copyright 2019 Yunion +// +// 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 ansiblev2 + +import "testing" + +func TestRequirementsString(t *testing.T) { + rss := []RoleSource{ + { + Src: "zaiste.nginx", + }, + { + Src: "geerlingguy.gitlab", + Version: "1.1.0", + }, + { + Name: "essentials", + Src: "https://github.com/zaiste/ansible-essentials", + }, + { + Name: "java", + Src: "https://github.com/geerlingguy/ansible-role-java", + Version: "origin/master", + }, + { + Name: "jenkins", + Src: "https://github.com/geerlingguy/ansible-role-jenkins", + Version: "4.2.1", + }, + } + rm := NewRequirements(rss...) + t.Logf("\n%s", rm.String()) +} diff --git a/pkg/util/ansiblev2/session.go b/pkg/util/ansiblev2/session.go index b4f494536f..ff60fda5be 100644 --- a/pkg/util/ansiblev2/session.go +++ b/pkg/util/ansiblev2/session.go @@ -29,10 +29,11 @@ import ( ) type Session struct { - privateKey string - playbook string - inventory string - files map[string][]byte + privateKey string + playbook string + inventory string + requirements string + files map[string][]byte outputWriter io.Writer stateMux *sync.Mutex @@ -63,6 +64,11 @@ func (sess *Session) Inventory(s string) *Session { return sess } +func (sess *Session) Requirements(s string) *Session { + sess.requirements = s + return sess +} + func (sess *Session) AddFile(path string, data []byte) *Session { sess.files[path] = data return sess @@ -143,6 +149,17 @@ func (sess *Session) Run(ctx context.Context) (err error) { } } + // write out requirements + var requirements string + if len(sess.requirements) > 0 { + requirements = filepath.Join(tmpdir, "requirements.yml") + err = ioutil.WriteFile(requirements, []byte(sess.requirements), os.FileMode(0600)) + if err != nil { + err = errors.WithMessagef(err, "writing requirements %s", requirements) + return + } + } + // write out files for name, content := range sess.files { path := filepath.Join(tmpdir, name) @@ -167,6 +184,29 @@ func (sess *Session) Run(ctx context.Context) (err error) { } }() + // install required roles + if len(requirements) > 0 { + args := []string{ + "install", "-r", requirements, + } + cmd := exec.CommandContext(ctx, "ansible-galaxy", args...) + stdout, _ := cmd.StdoutPipe() + stderr, _ := cmd.StderrPipe() + if err1 := cmd.Start(); err1 != nil { + errs = append(errs, errors.WithMessage(err1, "start ansible-galaxy install roles")) + return + } + // Mix stdout, stderr + if sess.outputWriter != nil { + go io.Copy(sess.outputWriter, stdout) + go io.Copy(sess.outputWriter, stderr) + } + if err1 := cmd.Wait(); err1 != nil { + errs = append(errs, errors.WithMessage(err1, "wait ansible-galaxy install roles")) + } + } + + // run playbook { args := []string{ "--inventory", inventory, From faf32415fa2559abd736bb160aa0956b29d75770 Mon Sep 17 00:00:00 2001 From: Rain Date: Thu, 30 Apr 2020 00:27:49 +0800 Subject: [PATCH 2/4] feat/ansible_pb: Add IncludeRole implments 'ITask' in pkg/util/ansiblev2 --- pkg/util/ansiblev2/playbook.go | 23 +++++++++++++++++++++++ pkg/util/ansiblev2/playbook_test.go | 8 ++++++++ 2 files changed, 31 insertions(+) diff --git a/pkg/util/ansiblev2/playbook.go b/pkg/util/ansiblev2/playbook.go index 8bbfea1db6..fdc4917208 100644 --- a/pkg/util/ansiblev2/playbook.go +++ b/pkg/util/ansiblev2/playbook.go @@ -104,6 +104,29 @@ func (t *ShellTask) MarshalYAML() (interface{}, error) { return r, nil } +type IncludeRole struct { + Name string + Tags []string + Vars map[string]interface{} + When string +} + +func (r *IncludeRole) MarshalYAML() (interface{}, error) { + out := map[string]interface{}{ + "include_role": map[string]interface{}{"name": r.Name}, + } + if len(r.Tags) > 0 { + out["tags"] = r.Tags + } + if len(r.Vars) > 0 { + out["vars"] = r.Vars + } + if len(r.When) > 0 { + out["when"] = r.When + } + return out, nil +} + type Block struct { Name string WithPlugin string diff --git a/pkg/util/ansiblev2/playbook_test.go b/pkg/util/ansiblev2/playbook_test.go index 87c184b66f..36958475f0 100644 --- a/pkg/util/ansiblev2/playbook_test.go +++ b/pkg/util/ansiblev2/playbook_test.go @@ -59,6 +59,14 @@ func TestPlaybookString(t *testing.T) { "group": "root", }, }, + &IncludeRole{ + Name: "foo_app_instance", + Vars: map[string]interface{}{ + "dir": "/opt/a", + }, + Tags: []string{"bar", "baz"}, + When: "ansible_facts['os_family'] == 'RedHat'", + }, ) play.Hosts = "all" configureBlock := NewBlock( From cf514364eb44402acd23259b464e3e1646c1c9cb Mon Sep 17 00:00:00 2001 From: Rain Date: Fri, 8 May 2020 11:01:31 +0800 Subject: [PATCH 3/4] feat/ansible_pb: Add climc operator for ansibleplaybooks_v2 1) ansibleplaybooksv2-run 2) ansibleplaybooksv2-stop 3) ansibleplaybooksv2-delete --- .../shell/ansible/ansibleplaybooks_v2.go | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/cmd/climc/shell/ansible/ansibleplaybooks_v2.go b/cmd/climc/shell/ansible/ansibleplaybooks_v2.go index f28dc71534..13c9444f03 100644 --- a/cmd/climc/shell/ansible/ansibleplaybooks_v2.go +++ b/cmd/climc/shell/ansible/ansibleplaybooks_v2.go @@ -49,4 +49,34 @@ func init() { printList(apbs, modules.AnsiblePlaybooksV2.GetColumns(s)) return nil }) + R(&AnsiblePlaybookV2IdOptions{}, "ansibleplaybookv2-run", "Run ansible playbook", + func(s *mcclient.ClientSession, opts *AnsiblePlaybookV2IdOptions) error { + apb, err := modules.AnsiblePlaybooksV2.PerformAction(s, opts.ID, "run", nil) + if err != nil { + return err + } + printObject(apb) + return nil + }, + ) + R(&AnsiblePlaybookV2IdOptions{}, "ansibleplaybookv2-stop", "Stop ansible playbook", + func(s *mcclient.ClientSession, opts *AnsiblePlaybookV2IdOptions) error { + apb, err := modules.AnsiblePlaybooksV2.PerformAction(s, opts.ID, "stop", nil) + if err != nil { + return err + } + printObject(apb) + return nil + }, + ) + R(&AnsiblePlaybookV2IdOptions{}, "ansibleplaybookv2-delete", "Delete ansible playbook", + func(s *mcclient.ClientSession, opts *AnsiblePlaybookV2IdOptions) error { + apb, err := modules.AnsiblePlaybooksV2.Delete(s, opts.ID, nil) + if err != nil { + return err + } + printObject(apb) + return nil + }, + ) } From 1fbfc7f22062aee36c576c15334dc6dee673d175 Mon Sep 17 00:00:00 2001 From: Rain Date: Fri, 8 May 2020 11:07:08 +0800 Subject: [PATCH 4/4] feat/ansibleserver: Install git in Dockerfile.ansibleserver-base --- build/docker/Dockerfile.ansibleserver-base | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/docker/Dockerfile.ansibleserver-base b/build/docker/Dockerfile.ansibleserver-base index 2bed98155b..1ff258d2ba 100644 --- a/build/docker/Dockerfile.ansibleserver-base +++ b/build/docker/Dockerfile.ansibleserver-base @@ -7,7 +7,7 @@ ENV TZ Asia/Shanghai RUN mkdir -p /opt/yunion/bin RUN apk update && \ - apk add --no-cache tzdata ansible ca-certificates && \ + apk add --no-cache tzdata git ansible ca-certificates && \ rm -rf /var/cache/apk/* RUN cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime