feat: 简单执行ansible命令或者ansible-playbook剧本
This commit is contained in:
@@ -1 +1,54 @@
|
|||||||
# kenaito-ansible-go-example
|
# go-ansible库应用
|
||||||
|
|
||||||
|
## thank you for go-ansible authors
|
||||||
|
|
||||||
|
https://github.com/apenella/go-ansible
|
||||||
|
|
||||||
|
## 执行结果参考
|
||||||
|
|
||||||
|
#### 执行成功
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"Command": "ansible all --args 'ping 127.0.0.1 -c 2' --inventory 192.168.0.1,192.168.0.2 --module-name command --connection local",
|
||||||
|
"Success": true,
|
||||||
|
"Data": {
|
||||||
|
"192.168.0.1": {
|
||||||
|
"changed": 1,
|
||||||
|
"failures": 0,
|
||||||
|
"ignored": 0,
|
||||||
|
"ok": 1,
|
||||||
|
"rescued": 0,
|
||||||
|
"skipped": 0,
|
||||||
|
"unreachable": 0
|
||||||
|
},
|
||||||
|
"192.168.0.2": {
|
||||||
|
"changed": 1,
|
||||||
|
"failures": 0,
|
||||||
|
"ignored": 0,
|
||||||
|
"ok": 1,
|
||||||
|
"rescued": 0,
|
||||||
|
"skipped": 0,
|
||||||
|
"unreachable": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ErrMsg": "",
|
||||||
|
"StartTime": "2025-07-03 21:17:26",
|
||||||
|
"EndTime": "2025-07-03 21:17:28",
|
||||||
|
"Duration": 1566
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 执行失败
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"Command": "ansible all --args 'pingxxxx 127.0.0.1 -c 2' --inventory 192.168.0.1,192.168.0.2 --module-name command --connection local",
|
||||||
|
"Success": false,
|
||||||
|
"Data": null,
|
||||||
|
"ErrMsg": "Error during command execution.\n Command executed: ansible all --args 'pingxxxx 127.0.0.1 -c 2' --inventory 192.168.0.1,192.168.0.2 --module-name command --connection local\n\n Environment variables:\nANSIBLE_LOAD_CALLBACK_PLUGINS=1\nANSIBLE_STDOUT_CALLBACK=json\n\n\texit status 2",
|
||||||
|
"StartTime": "2025-07-03 21:20:07",
|
||||||
|
"EndTime": "2025-07-03 21:20:08",
|
||||||
|
"Duration": 497
|
||||||
|
}
|
||||||
|
```
|
||||||
@@ -0,0 +1,105 @@
|
|||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"github.com/apenella/go-ansible/v2/pkg/adhoc"
|
||||||
|
"github.com/apenella/go-ansible/v2/pkg/execute"
|
||||||
|
results "github.com/apenella/go-ansible/v2/pkg/execute/result/json"
|
||||||
|
"io"
|
||||||
|
"kenaito-ansible-go-example/model"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ExecuteCommand
|
||||||
|
// mode 模式。local、ssh、smart
|
||||||
|
// inventoryList 主机列表。
|
||||||
|
// moduleName 模块名称。
|
||||||
|
// args 模块参数。
|
||||||
|
// extraVars 变量。
|
||||||
|
func ExecuteCommand(mode string, inventoryList []string, moduleName string, args string, extraVars map[string]interface{}) model.CsExecResult {
|
||||||
|
ansibleAdhocOptions := &adhoc.AnsibleAdhocOptions{
|
||||||
|
Connection: mode,
|
||||||
|
Inventory: strings.Join(inventoryList, ","),
|
||||||
|
ModuleName: moduleName,
|
||||||
|
Args: args,
|
||||||
|
ExtraVars: extraVars,
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
"all" 表示该命令将作用于 Inventory(清单)中的所有主机。
|
||||||
|
"webservers"(针对 webservers 组的主机)
|
||||||
|
"192.168.1.1"(针对特定 IP 的主机)
|
||||||
|
"db*"(匹配所有以 db 开头的主机名)
|
||||||
|
*/
|
||||||
|
adhocCmd := adhoc.NewAnsibleAdhocCmd(
|
||||||
|
adhoc.WithPattern("all"),
|
||||||
|
adhoc.WithAdhocOptions(ansibleAdhocOptions),
|
||||||
|
)
|
||||||
|
|
||||||
|
result := model.CsExecResult{}
|
||||||
|
result.Command = adhocCmd.String()
|
||||||
|
|
||||||
|
buff := new(bytes.Buffer)
|
||||||
|
defaultExecute := execute.NewDefaultExecute(
|
||||||
|
execute.WithCmd(adhocCmd),
|
||||||
|
execute.WithEnvVars(map[string]string{
|
||||||
|
"ANSIBLE_LOAD_CALLBACK_PLUGINS": "1",
|
||||||
|
"ANSIBLE_STDOUT_CALLBACK": "json",
|
||||||
|
}),
|
||||||
|
execute.WithWrite(buff),
|
||||||
|
)
|
||||||
|
startTime := time.Now()
|
||||||
|
timeLayout := "2006-01-02 15:04:05"
|
||||||
|
result.StartTime = startTime.Format(timeLayout)
|
||||||
|
err := defaultExecute.Execute(context.TODO())
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("ansible命令行参数异常: %v\n", err)
|
||||||
|
result.Success = false
|
||||||
|
result.ErrMsg = err.Error()
|
||||||
|
endTime := time.Now()
|
||||||
|
result.EndTime = endTime.Format(timeLayout)
|
||||||
|
result.Duration = int(endTime.Sub(startTime).Milliseconds())
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析的结果有问题
|
||||||
|
res, err := results.ParseJSONResultsStream(io.Reader(buff))
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("执行结果解析异常: %v\n", err)
|
||||||
|
result.Success = false
|
||||||
|
result.ErrMsg = err.Error()
|
||||||
|
endTime := time.Now()
|
||||||
|
result.EndTime = endTime.Format(timeLayout)
|
||||||
|
result.Duration = int(endTime.Sub(startTime).Milliseconds())
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
result.Success = true
|
||||||
|
result.Data = res.Stats
|
||||||
|
endTime := time.Now()
|
||||||
|
result.EndTime = endTime.Format(timeLayout)
|
||||||
|
result.Duration = int(endTime.Sub(startTime).Milliseconds())
|
||||||
|
return result
|
||||||
|
|
||||||
|
// 自行解析,也有问题,降级吧
|
||||||
|
//var data map[string]interface{}
|
||||||
|
//jsonStr, err := util.ReaderToString(io.Reader(buff))
|
||||||
|
//fmt.Printf("jsonStr => %s\n\n", jsonStr)
|
||||||
|
//err = json.Unmarshal([]byte(jsonStr), &data)
|
||||||
|
//if err != nil {
|
||||||
|
// fmt.Printf("执行结果解析异常: %v\n", err)
|
||||||
|
// result.Success = false
|
||||||
|
// result.ErrMsg = err.Error()
|
||||||
|
// endTime := time.Now()
|
||||||
|
// result.EndTime = endTime.Format(timeLayout)
|
||||||
|
// result.Duration = int(endTime.Sub(startTime).Milliseconds())
|
||||||
|
// return result
|
||||||
|
//}
|
||||||
|
//result.Success = true
|
||||||
|
//result.Data = data["stats"]
|
||||||
|
//endTime := time.Now()
|
||||||
|
//result.EndTime = endTime.Format(timeLayout)
|
||||||
|
//result.Duration = int(endTime.Sub(startTime).Milliseconds())
|
||||||
|
//return result
|
||||||
|
}
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"github.com/apenella/go-ansible/v2/pkg/execute"
|
||||||
|
"github.com/apenella/go-ansible/v2/pkg/execute/measure"
|
||||||
|
results "github.com/apenella/go-ansible/v2/pkg/execute/result/json"
|
||||||
|
"github.com/apenella/go-ansible/v2/pkg/execute/stdoutcallback"
|
||||||
|
"github.com/apenella/go-ansible/v2/pkg/playbook"
|
||||||
|
"io"
|
||||||
|
"kenaito-ansible-go-example/model"
|
||||||
|
"kenaito-ansible-go-example/util"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ExecutePlaybook
|
||||||
|
// mode 模式。local、ssh、smart
|
||||||
|
// inventoryList 主机列表。
|
||||||
|
// playbookList playbook脚本列表。
|
||||||
|
// extraVars 变量。
|
||||||
|
func ExecutePlaybook(mode string, inventoryList []string, playbookList []string, extraVars map[string]interface{}) model.CsExecResult {
|
||||||
|
ansiblePlaybookOptions := &playbook.AnsiblePlaybookOptions{
|
||||||
|
Connection: mode,
|
||||||
|
Inventory: strings.Join(inventoryList, ","),
|
||||||
|
ExtraVars: extraVars,
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
"all" 表示该命令将作用于 Inventory(清单)中的所有主机。
|
||||||
|
"webservers"(针对 webservers 组的主机)
|
||||||
|
"192.168.1.1"(针对特定 IP 的主机)
|
||||||
|
"db*"(匹配所有以 db 开头的主机名)
|
||||||
|
*/
|
||||||
|
playbookCmd := playbook.NewAnsiblePlaybookCmd(
|
||||||
|
playbook.WithPlaybooks(playbookList...),
|
||||||
|
playbook.WithPlaybookOptions(ansiblePlaybookOptions),
|
||||||
|
)
|
||||||
|
|
||||||
|
result := model.CsExecResult{}
|
||||||
|
result.Command = playbookCmd.String()
|
||||||
|
|
||||||
|
buff := new(bytes.Buffer)
|
||||||
|
exec := measure.NewExecutorTimeMeasurement(
|
||||||
|
stdoutcallback.NewJSONStdoutCallbackExecute(
|
||||||
|
execute.NewDefaultExecute(
|
||||||
|
execute.WithCmd(playbookCmd),
|
||||||
|
execute.WithErrorEnrich(playbook.NewAnsiblePlaybookErrorEnrich()),
|
||||||
|
execute.WithWrite(io.Writer(buff)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
startTime := time.Now()
|
||||||
|
result.StartTime = util.FormatDateTime(startTime)
|
||||||
|
err := exec.Execute(context.TODO())
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("ansible-playbook命令行参数异常: %v\n", err)
|
||||||
|
result.Success = false
|
||||||
|
result.ErrMsg = err.Error()
|
||||||
|
endTime := time.Now()
|
||||||
|
result.EndTime = util.FormatDateTime(endTime)
|
||||||
|
result.Duration = int(endTime.Sub(startTime).Milliseconds())
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := results.ParseJSONResultsStream(io.Reader(buff))
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("执行结果解析异常: %v\n", err)
|
||||||
|
result.Success = false
|
||||||
|
result.ErrMsg = err.Error()
|
||||||
|
endTime := time.Now()
|
||||||
|
result.EndTime = util.FormatDateTime(endTime)
|
||||||
|
result.Duration = int(endTime.Sub(startTime).Milliseconds())
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
result.Success = true
|
||||||
|
result.Data = res.Stats
|
||||||
|
endTime := time.Now()
|
||||||
|
result.EndTime = util.FormatDateTime(endTime)
|
||||||
|
result.Duration = int(endTime.Sub(startTime).Milliseconds())
|
||||||
|
return result
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
module kenaito-ansible-go-example
|
||||||
|
|
||||||
|
go 1.23.3
|
||||||
|
|
||||||
|
require github.com/apenella/go-ansible/v2 v2.2.0
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/apenella/go-common-utils/data v0.0.0-20220913191136-86daaa87e7df // indirect
|
||||||
|
github.com/apenella/go-common-utils/error v0.0.0-20220913191136-86daaa87e7df // indirect
|
||||||
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
|
github.com/stretchr/objx v0.5.2 // indirect
|
||||||
|
github.com/stretchr/testify v1.9.0 // indirect
|
||||||
|
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
|
)
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
github.com/apenella/go-ansible/v2 v2.2.0 h1:IldFymiRy9hc+rCyZ0dj4jJfLUmW9wjzU4JxoOMy9KE=
|
||||||
|
github.com/apenella/go-ansible/v2 v2.2.0/go.mod h1:G3JNiTazO5t/PEriJFywV/4RFu2RtzAxxR7Z5SoYF78=
|
||||||
|
github.com/apenella/go-common-utils/data v0.0.0-20220913191136-86daaa87e7df h1:sEikY2P+NZK/7VZUwIsnXIGElhsuFDSxh1bZYwHxdcI=
|
||||||
|
github.com/apenella/go-common-utils/data v0.0.0-20220913191136-86daaa87e7df/go.mod h1:cLVL6GjUiKG/WyBzX+KD6h/XRV/HnNZIZbMNNiBgQ9o=
|
||||||
|
github.com/apenella/go-common-utils/error v0.0.0-20220913191136-86daaa87e7df h1:SvlYbjlsSQDS7hbVT1h012/zdgvcwWJ+Yd9XRiiY/8s=
|
||||||
|
github.com/apenella/go-common-utils/error v0.0.0-20220913191136-86daaa87e7df/go.mod h1:+3dyIlHX350xJIUIffwMLswZXU+N2FwDE05VuKqxYdw=
|
||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
||||||
|
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||||
|
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=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||||
|
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"kenaito-ansible-go-example/core"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
testExecuteCommand()
|
||||||
|
//testExecutePlaybook()
|
||||||
|
}
|
||||||
|
func testExecuteCommand() {
|
||||||
|
command := core.ExecuteCommand(
|
||||||
|
"local",
|
||||||
|
[]string{"192.168.150.170", "192.168.205.234"},
|
||||||
|
"command",
|
||||||
|
"pingxxxx 127.0.0.1 -c 2",
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
marshal, err := json.Marshal(command)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Error marshalling to JSON:", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fmt.Printf("Execute Result: %s\n", string(marshal))
|
||||||
|
}
|
||||||
|
func testExecutePlaybook() {
|
||||||
|
command := core.ExecutePlaybook(
|
||||||
|
"local",
|
||||||
|
[]string{"192.168.150.170", "192.168.205.234"},
|
||||||
|
[]string{"site1.yml", "site2.yml", "site3.yml"},
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
marshal, err := json.Marshal(command)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Error marshalling to JSON:", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fmt.Printf("Execute Playbook Result: %s\n", string(marshal))
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
type CsExecResult struct {
|
||||||
|
// 执行的ansible命令
|
||||||
|
Command string
|
||||||
|
// 执行成功与否
|
||||||
|
Success bool
|
||||||
|
// 执行结果
|
||||||
|
//ok 表示任务执行成功,没有做任何修改;
|
||||||
|
//changed 表示任务执行成功,并且做了一些修改;
|
||||||
|
//failed 表示任务执行失败;
|
||||||
|
//unreachable 表示任务无法连接到目标主机。
|
||||||
|
Data interface{}
|
||||||
|
// 异常信息
|
||||||
|
ErrMsg string
|
||||||
|
// 开始时间
|
||||||
|
//StartTime time.Time
|
||||||
|
StartTime string
|
||||||
|
// 结束时间
|
||||||
|
//EndTime time.Time
|
||||||
|
EndTime string
|
||||||
|
// 执行耗时
|
||||||
|
Duration int
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
---
|
||||||
|
|
||||||
|
- hosts: all
|
||||||
|
gather_facts: false
|
||||||
|
vars:
|
||||||
|
array:
|
||||||
|
- Your are running
|
||||||
|
- 'ansibleplaybook-json-stdout'
|
||||||
|
- first example
|
||||||
|
map:
|
||||||
|
intro: Your are running
|
||||||
|
what: ansibleplaybook-json-stdout
|
||||||
|
outro: first example
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
- name: ansibleplaybook-json-stdout
|
||||||
|
debug:
|
||||||
|
msg: |
|
||||||
|
Your are running
|
||||||
|
'ansibleplaybook-json-stdout'
|
||||||
|
first example
|
||||||
|
|
||||||
|
- name: ansibleplaybook-json-stdout from array
|
||||||
|
debug:
|
||||||
|
msg: "{{ array }}"
|
||||||
|
|
||||||
|
- name: ansibleplaybook-json-stdout from map
|
||||||
|
debug:
|
||||||
|
msg: "{{ map }}"
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
---
|
||||||
|
|
||||||
|
- hosts: all
|
||||||
|
gather_facts: false
|
||||||
|
vars:
|
||||||
|
array:
|
||||||
|
- Your are running
|
||||||
|
- 'ansibleplaybook-json-stdout'
|
||||||
|
- second example
|
||||||
|
map:
|
||||||
|
intro: Your are running
|
||||||
|
what: ansibleplaybook-json-stdout
|
||||||
|
outro: second example
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
- name: ansibleplaybook-json-stdout
|
||||||
|
debug:
|
||||||
|
msg: |
|
||||||
|
Your are running
|
||||||
|
'ansibleplaybook-json-stdout'
|
||||||
|
second example
|
||||||
|
|
||||||
|
- name: ansibleplaybook-json-stdout from array
|
||||||
|
debug:
|
||||||
|
msg: "{{ array }}"
|
||||||
|
|
||||||
|
- name: ansibleplaybook-json-stdout from map
|
||||||
|
debug:
|
||||||
|
msg: "{{ map }}"
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
---
|
||||||
|
|
||||||
|
- hosts: all
|
||||||
|
gather_facts: false
|
||||||
|
vars:
|
||||||
|
array:
|
||||||
|
- Your are running
|
||||||
|
- 'ansibleplaybook-json-stdout'
|
||||||
|
- third example
|
||||||
|
map:
|
||||||
|
intro: Your are running
|
||||||
|
what: ansibleplaybook-json-stdout
|
||||||
|
outro: third example
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
- name: ansibleplaybook-json-stdout
|
||||||
|
debug:
|
||||||
|
msg: |
|
||||||
|
Your are running
|
||||||
|
'ansibleplaybook-json-stdout'
|
||||||
|
third example
|
||||||
|
|
||||||
|
- name: ansibleplaybook-json-stdout from array
|
||||||
|
debug:
|
||||||
|
msg: "{{ array }}"
|
||||||
|
|
||||||
|
- name: ansibleplaybook-json-stdout from map
|
||||||
|
debug:
|
||||||
|
msg: "{{ map }}"
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package util
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ReaderToString(reader io.Reader) (string, error) {
|
||||||
|
data, err := ioutil.ReadAll(reader)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return string(data), nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package util
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
const layoutDateTime = "2006-01-02 15:04:05"
|
||||||
|
|
||||||
|
func FormatDateTime(time time.Time) string {
|
||||||
|
return time.Format(layoutDateTime)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user