336 lines
8.6 KiB
Go
336 lines
8.6 KiB
Go
package plugin
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"git.sr.ht/~akilan1999/p2p-rendering-computation/client"
|
|
"git.sr.ht/~akilan1999/p2p-rendering-computation/config"
|
|
"github.com/google/uuid"
|
|
"gopkg.in/yaml.v2"
|
|
"io/ioutil"
|
|
"strconv"
|
|
|
|
"github.com/apenella/go-ansible/pkg/execute"
|
|
"github.com/apenella/go-ansible/pkg/options"
|
|
"github.com/apenella/go-ansible/pkg/playbook"
|
|
"github.com/apenella/go-ansible/pkg/stdoutcallback/results"
|
|
"github.com/otiai10/copy"
|
|
)
|
|
|
|
// Plugins Array of all plugins detected
|
|
type Plugins struct {
|
|
PluginsDetected []*Plugin
|
|
}
|
|
|
|
// Plugin Information about the plugins available
|
|
type Plugin struct {
|
|
FolderName string
|
|
PluginDescription string
|
|
path string
|
|
Execute []*ExecuteIP
|
|
}
|
|
|
|
// ExecuteIP IP Address to execute Ansible instruction
|
|
type ExecuteIP struct {
|
|
IPAddress string
|
|
SSHPortNo string
|
|
Success bool
|
|
}
|
|
|
|
// Host Struct for ansible host
|
|
// Generated from https://zhwt.github.io/yaml-to-go/
|
|
type Host struct {
|
|
All struct {
|
|
Vars struct {
|
|
AnsiblePythonInterpreter string `yaml:"ansible_python_interpreter"`
|
|
} `yaml:"vars"`
|
|
} `yaml:"all"`
|
|
Main struct {
|
|
Hosts struct {
|
|
Host1 struct {
|
|
AnsibleHost string `yaml:"ansible_host"`
|
|
AnsiblePort int `yaml:"ansible_port"`
|
|
AnsibleUser string `yaml:"ansible_user"`
|
|
AnsibleSSHPass string `yaml:"ansible_ssh_pass"`
|
|
AnsibleSudoPass string `yaml:"ansible_sudo_pass"`
|
|
} `yaml:"host1"`
|
|
} `yaml:"hosts"`
|
|
} `yaml:"main"`
|
|
}
|
|
|
|
// DetectPlugins Detects all the plugins available
|
|
func DetectPlugins()(*Plugins,error){
|
|
config, err:= config.ConfigInit()
|
|
if err != nil {
|
|
return nil,err
|
|
}
|
|
folders, err := ioutil.ReadDir(config.PluginPath)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var plugins *Plugins = new(Plugins)
|
|
|
|
for _, f := range folders {
|
|
if f.IsDir() {
|
|
//Declare variable plugin of type Plugin
|
|
var plugin Plugin
|
|
|
|
// Setting name of folder to plugin
|
|
plugin.FolderName = f.Name()
|
|
// Getting Description from file description.txt
|
|
Description, err := ioutil.ReadFile(config.PluginPath + "/" + plugin.FolderName + "/description.txt")
|
|
// if we os.Open returns an error then handle it
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Get Description from description.txt
|
|
plugin.PluginDescription = string(Description)
|
|
|
|
plugins.PluginsDetected = append(plugins.PluginsDetected, &plugin)
|
|
}
|
|
}
|
|
|
|
return plugins,nil
|
|
}
|
|
|
|
// RunPlugin Executes plugins based on the plugin name provided
|
|
func RunPlugin(pluginName string ,IPAddresses []*ExecuteIP) (*Plugin,error) {
|
|
plugins, err := DetectPlugins()
|
|
if err != nil {
|
|
return nil,err
|
|
}
|
|
|
|
// Variable to store struct information about the plugin
|
|
var plugindetected *Plugin
|
|
for _,plugin := range plugins.PluginsDetected {
|
|
if plugin.FolderName == pluginName {
|
|
plugindetected = plugin
|
|
plugindetected.Execute = IPAddresses
|
|
// Get Execute plugin path from config file
|
|
config, err := config.ConfigInit()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
plugindetected.path = config.PluginPath
|
|
break
|
|
}
|
|
}
|
|
|
|
if plugindetected == nil {
|
|
return nil, errors.New("Plugin not detected")
|
|
}
|
|
|
|
|
|
// Create copy of the plugin the tmp directory
|
|
// To ensure we execute the plugin from there
|
|
err = plugindetected.CopyToTmpPlugin()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Executing the plugin
|
|
err = plugindetected.ExecutePlugin()
|
|
if err != nil {
|
|
return nil,err
|
|
}
|
|
|
|
return plugindetected,nil
|
|
}
|
|
|
|
// ExecutePlugin Function to execute plugins that are called
|
|
func (p *Plugin) ExecutePlugin() error {
|
|
|
|
// Run ip address to execute ansible inside
|
|
for _,execute := range p.Execute {
|
|
// Modify ansible hosts before executing
|
|
err := execute.ModifyHost(p)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = execute.RunAnsible(p)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
// If ran successfully then change success flag to true
|
|
execute.Success = true
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// RunAnsible Executes based on credentials on the struct
|
|
func (e *ExecuteIP)RunAnsible(p *Plugin) error {
|
|
ansiblePlaybookConnectionOptions := &options.AnsibleConnectionOptions{
|
|
User: "master",
|
|
}
|
|
|
|
ansiblePlaybookOptions := &playbook.AnsiblePlaybookOptions{
|
|
Inventory: p.path + "/" + p.FolderName + "/hosts",
|
|
ExtraVars: map[string]interface{}{"host_key_checking":"false"},
|
|
}
|
|
|
|
ansiblePlaybookPrivilegeEscalationOptions := &options.AnsiblePrivilegeEscalationOptions{
|
|
Become: true,
|
|
}
|
|
|
|
playbook := &playbook.AnsiblePlaybookCmd{
|
|
Playbooks: []string{p.path + "/" + p.FolderName + "/site.yml"},
|
|
ConnectionOptions: ansiblePlaybookConnectionOptions,
|
|
PrivilegeEscalationOptions: ansiblePlaybookPrivilegeEscalationOptions,
|
|
Options: ansiblePlaybookOptions,
|
|
Exec: execute.NewDefaultExecute(
|
|
execute.WithTransformers(
|
|
results.Prepend("success"),
|
|
),
|
|
),
|
|
}
|
|
|
|
err := playbook.Run(context.TODO())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// ModifyHost adds IP address , port no to the config file
|
|
func (e *ExecuteIP)ModifyHost(p *Plugin) error {
|
|
host,err := ReadHost(p.path + "/" + p.FolderName + "/hosts")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
// Setting ansible host
|
|
host.Main.Hosts.Host1.AnsibleHost = e.IPAddress
|
|
// Setting SSH port no
|
|
host.Main.Hosts.Host1.AnsiblePort, err = strconv.Atoi(e.SSHPortNo)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
// Setting SSH user name
|
|
host.Main.Hosts.Host1.AnsibleUser = "master"
|
|
// Setting SSH password
|
|
host.Main.Hosts.Host1.AnsibleSSHPass = "password"
|
|
// Setting SSH sudo password
|
|
host.Main.Hosts.Host1.AnsibleSudoPass = "password"
|
|
|
|
// write modified information to the hosts yaml file
|
|
data,err := yaml.Marshal(host)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = ioutil.WriteFile(p.path + "/" + p.FolderName + "/hosts",data,0777)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// ReadHost Reads host file and adds
|
|
func ReadHost(filename string) (*Host, error) {
|
|
buf, err := ioutil.ReadFile(filename)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
c := &Host{}
|
|
err = yaml.Unmarshal(buf, c)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("in file %q: %v", filename, err)
|
|
}
|
|
|
|
return c, nil
|
|
}
|
|
|
|
// RunPluginContainer Runs ansible plugin based on plugin name and contianer name which
|
|
// is derived from the tracked containers file
|
|
func RunPluginContainer(PluginName string, ContainerID string) error {
|
|
// Gets container information based on container ID
|
|
ContainerInformation, err := client.GetContainerInformation(ContainerID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Setting Up IP's for which the plugins will be executed
|
|
var ExecuteIPs []*ExecuteIP
|
|
var ExecuteIP ExecuteIP
|
|
// Getting port no of SSH port
|
|
for _,port := range ContainerInformation.Container.Ports.PortSet {
|
|
if port.PortName == "SSH" {
|
|
ExecuteIP.SSHPortNo = fmt.Sprint(port.ExternalPort)
|
|
break
|
|
}
|
|
}
|
|
// Handle error if SSH port is not provided
|
|
if ExecuteIP.SSHPortNo == "" {
|
|
return errors.New("SSH port not found")
|
|
}
|
|
// IP address of the container
|
|
ExecuteIP.IPAddress = ContainerInformation.IpAddress
|
|
// Append IP to list of executor IP
|
|
ExecuteIPs = append(ExecuteIPs, &ExecuteIP)
|
|
// Run plugin to execute plugin
|
|
_, err = RunPlugin(PluginName,ExecuteIPs)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// CheckRunPlugin Checks if the ID belongs to the group or container
|
|
// calls the plugin function the appropriate amount of times
|
|
func CheckRunPlugin(PluginName string, ID string) error {
|
|
// Check if the ID belongs to the group or container ID
|
|
id, err := client.CheckID(ID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
// When the ID belongs to a group
|
|
if id == "group" {
|
|
// gets the group information
|
|
group, err := client.GetGroup(ID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
// Iterate through each container information in the group
|
|
// and run the plugin in each of them
|
|
for _, container := range group.TrackContainerList {
|
|
// runs plugin for each container
|
|
err := RunPluginContainer(PluginName, container.Id)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
} else { // This means the following ID is a container ID
|
|
err := RunPluginContainer(PluginName, ID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// CopyToTmpPlugin This function would ensure that we create a copy of the
|
|
// plugin in the tmp directory, and it would be executed
|
|
// from there. This due to the reason of automating port allocation
|
|
// when running plugins
|
|
func (p *Plugin)CopyToTmpPlugin() error {
|
|
// generate rand to UUID this is debug the ansible file if needed
|
|
id := uuid.New()
|
|
// copies the plugin to the tmp directory
|
|
err := copy.Copy(p.path+"/"+p.FolderName, "/tmp/"+ id.String() + "_" + p.FolderName)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Set the plugin execution to the tmp location
|
|
p.path = "/tmp"
|
|
p.FolderName = id.String() + "_" + p.FolderName
|
|
|
|
return nil
|
|
}
|