Skip to content

Instantly share code, notes, and snippets.

@sivel
Last active September 28, 2023 18:40
Show Gist options
  • Save sivel/ccd81bdfb31ca0c0e05d to your computer and use it in GitHub Desktop.
Save sivel/ccd81bdfb31ca0c0e05d to your computer and use it in GitHub Desktop.
Ansible Binary Golang Module
go build helloworld.go
GOOS=windows GOARCH=amd64 go build helloworld.go
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
)
type ModuleArgs struct {
Name string
}
type Response struct {
Msg string `json:"msg"`
Changed bool `json:"changed"`
Failed bool `json:"failed"`
}
func ExitJson(responseBody Response) {
returnResponse(responseBody)
}
func FailJson(responseBody Response) {
responseBody.Failed = true
returnResponse(responseBody)
}
func returnResponse(responseBody Response) {
var response []byte
var err error
response, err = json.Marshal(responseBody)
if err != nil {
response, _ = json.Marshal(Response{Msg: "Invalid response object"})
}
fmt.Println(string(response))
if responseBody.Failed {
os.Exit(1)
} else {
os.Exit(0)
}
}
func main() {
var response Response
if len(os.Args) != 2 {
response.Msg = "No argument file provided"
FailJson(response)
}
argsFile := os.Args[1]
text, err := ioutil.ReadFile(argsFile)
if err != nil {
response.Msg = "Could not read configuration file: " + argsFile
FailJson(response)
}
var moduleArgs ModuleArgs
err = json.Unmarshal(text, &moduleArgs)
if err != nil {
response.Msg = "Configuration file not valid JSON: " + argsFile
FailJson(response)
}
var name string = "World"
if moduleArgs.Name != "" {
name = moduleArgs.Name
}
response.Msg = "Hello, " + name + "!"
ExitJson(response)
}
---
- hosts: all
gather_facts: false
tasks:
- name: ping
ping:
when: ansible_connection|default('ssh') != 'winrm'
- name: win_ping
win_ping:
when: ansible_connection|default('ssh') == 'winrm'
- name: Hello, World!
helloworld:
- name: Hello, Ansible!
helloworld:
name: Ansible
- name: Async Hello, World!
helloworld:
async: 1
poll: 1
when: ansible_connection|default('ssh') != 'winrm'
- name: Async Hello, Ansible!
helloworld:
name: Ansible
async: 1
poll: 1
when: ansible_connection|default('ssh') != 'winrm'
PLAY ***************************************************************************
TASK [ping] ********************************************************************
skipping: [windows] => {"changed": false, "skip_reason": "Conditional check failed", "skipped": true}
ok: [localhost] => {"changed": false, "ping": "pong"}
ok: [paramiko] => {"changed": false, "ping": "pong"}
ok: [ssh] => {"changed": false, "ping": "pong"}
TASK [win_ping] ****************************************************************
skipping: [paramiko] => {"changed": false, "skip_reason": "Conditional check failed", "skipped": true}
skipping: [ssh] => {"changed": false, "skip_reason": "Conditional check failed", "skipped": true}
ok: [localhost] => {"changed": false, "ping": "pong"}
ok: [windows] => {"changed": false, "ping": "pong"}
TASK [Hello, World!] ***********************************************************
ok: [localhost] => {"changed": false, "failed": false, "msg": "Hello, World!"}
ok: [ssh] => {"changed": false, "failed": false, "msg": "Hello, World!"}
ok: [paramiko] => {"changed": false, "failed": false, "msg": "Hello, World!"}
ok: [windows] => {"changed": false, "failed": false, "msg": "Hello, World!"}
TASK [Hello, Ansible!] *********************************************************
ok: [localhost] => {"changed": false, "failed": false, "msg": "Hello, Ansible!"}
ok: [ssh] => {"changed": false, "failed": false, "msg": "Hello, Ansible!"}
ok: [paramiko] => {"changed": false, "failed": false, "msg": "Hello, Ansible!"}
ok: [windows] => {"changed": false, "failed": false, "msg": "Hello, Ansible!"}
TASK [Async Hello, World!] *****************************************************
skipping: [windows] => {"changed": false, "skip_reason": "Conditional check failed", "skipped": true}
ok: [localhost] => {"ansible_job_id": "850503757820.24772", "changed": false, "failed": false, "finished": 1, "msg": "Hello, World!"}
ok: [ssh] => {"ansible_job_id": "772391860036.14904", "changed": false, "failed": false, "finished": 1, "msg": "Hello, World!"}
ok: [paramiko] => {"ansible_job_id": "579359157443.29225", "changed": false, "failed": false, "finished": 1, "msg": "Hello, World!"}
TASK [Async Hello, Ansible!] ***************************************************
skipping: [windows] => {"changed": false, "skip_reason": "Conditional check failed", "skipped": true}
ok: [localhost] => {"ansible_job_id": "936293883360.24795", "changed": false, "failed": false, "finished": 1, "msg": "Hello, Ansible!"}
ok: [ssh] => {"ansible_job_id": "636937348491.14933", "changed": false, "failed": false, "finished": 1, "msg": "Hello, Ansible!"}
ok: [paramiko] => {"ansible_job_id": "200792079727.29345", "changed": false, "failed": false, "finished": 1, "msg": "Hello, Ansible!"}
PLAY RECAP *********************************************************************
localhost : ok=5 changed=0 unreachable=0 failed=0
paramiko : ok=5 changed=0 unreachable=0 failed=0
ssh : ok=5 changed=0 unreachable=0 failed=0
windows : ok=3 changed=0 unreachable=0 failed=0
@owulveryck
Copy link

Hello,

I've create a module for ansible in go and I'm unable to use it in a playbook (module is missing interpreter line).
I've also tried your code without any success.

 ansible-playbook helloworld.yaml
 [WARNING]: provided hosts list is empty, only localhost is available


PLAY [localhost] ***************************************************************

TASK [ping] ********************************************************************
ok: [localhost]

TASK [win_ping] ****************************************************************
skipping: [localhost]

TASK [Hello, World!] ***********************************************************
fatal: [localhost]: FAILED! => {"failed": true, "msg": "module (helloworld) is missing interpreter line"}

NO MORE HOSTS LEFT *************************************************************
        to retry, use: --limit @helloworld.retry

PLAY RECAP *********************************************************************
localhost                  : ok=1    changed=0    unreachable=0    failed=1

It works with the test-module utility from ansible indeed.

How did you manage to get it work (and get your output) ?

Thank you for your help.

Olivier

@owulveryck
Copy link

owulveryck commented Apr 22, 2016

Thanks to thread in google groups, I've found your pull request ansible/ansible#13771.
I've applied it, and it's now working like a charm; thank you for your work.

@dmilind
Copy link

dmilind commented Jul 8, 2019

@owulveryck
could you reproduce the steps you did for solving this issue? I am also facing the same problem while running my tasks which calls golang binary. I am using ansible 2.8.0 on my mac. Things I have done

  1. Built golang binary and placed under library directory in one of my role.
  2. calling library as a module from ansible play.

@sivel
Copy link
Author

sivel commented Jul 8, 2019

@dmilind I'd recommend visiting the Ansible IRC channel or ansible-project mailing list.

Binary module support has existed for some time, and the binary module functionality is tested via CI.

@owulveryck
Copy link

@dmilind TBH I can hardly remember (made it a long time ago).
As far as I remember, I only imported the patch to my base ansible installation.

Sorry I cannot help you more, but i’m sure that the community will and that @sivel gave the best advice.

@serpro69
Copy link

I know this is kind of "old", but it's one of the few things I was getting when searching for "ansible modules in go". So, I've experimented with a few things and made some examples/suggestions on how to write/use/distribute ansible modules that are written in go. The examples are available here https://github.com/serpro69/golang-ansible-modules

If anyone has any other suggestions - please let me know. I don't like python very much, and go is much more pleasant to use. It would be very nice if ansible natively supported running .go files as modules, but that's probably not going to happen any time soon, so for now we need to do workarounds :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment