Compare commits
46
Commits
e50e00c2fa
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1a9bc04ef9 | ||
|
|
95f257a4d0 | ||
|
|
23e5e11a1d | ||
|
|
410010a974 | ||
|
|
2e763f0dd6 | ||
|
|
8aceabf0f1 | ||
|
|
204c4c783f | ||
|
|
9f41cbe454 | ||
|
|
747b022b9c | ||
|
|
dec7db5f5e | ||
|
|
389dcbc366 | ||
|
|
ec2b96105f | ||
|
|
77c0e5419c | ||
|
|
0e1294eb8e | ||
|
|
23931220bb | ||
|
|
00b9ce122d | ||
|
|
1072400433 | ||
|
|
83bbe32525 | ||
|
|
62ef2b7eb2 | ||
|
|
b03d18828c | ||
|
|
92d37b5217 | ||
|
|
c14a385a8a | ||
|
|
1f098de743 | ||
|
|
48f498a1dd | ||
|
|
75ffdefb4a | ||
|
|
c1bae790ed | ||
|
|
0124fc6ace | ||
|
|
a49427e88b | ||
|
|
a88d65d32f | ||
|
|
2294202009 | ||
|
|
5ca21e635d | ||
|
|
54ce83f458 | ||
|
|
20a59dceae | ||
|
|
ab3a912c5a | ||
|
|
e125ead164 | ||
|
|
2e19663a63 | ||
|
|
af4a1c46e1 | ||
|
|
bf8f562768 | ||
|
|
640a6d2a46 | ||
|
|
a889e1c7dd | ||
|
|
4833874324 | ||
|
|
05bd6f3c03 | ||
|
|
59d2508e7f | ||
|
|
91cc8ffe61 | ||
|
|
b762e10acb | ||
|
|
4e051565e2 |
@@ -0,0 +1,72 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
BAD
|
||||||
|
// Producer
|
||||||
|
|
||||||
|
type CustomerStorage interface {
|
||||||
|
StoreCustomer(customer string) error
|
||||||
|
GetCustomer(is string) error
|
||||||
|
UpdateCustomer(customer string) error
|
||||||
|
GetAllCustomers() ([]string,error)
|
||||||
|
GetCustomerWithoutContract() ([]string,error)
|
||||||
|
}
|
||||||
|
|
||||||
|
//======================================
|
||||||
|
|
||||||
|
|
||||||
|
// Consumer
|
||||||
|
|
||||||
|
type customerGetter interface {
|
||||||
|
GetAllCustomers() ([]string,error)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//===================================================================
|
||||||
|
|
||||||
|
type IntConfig struct {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c IntConfig) Get() int { <======== PRODUCER
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c IntConfig) Set(value int) {
|
||||||
|
//...
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Consumer
|
||||||
|
|
||||||
|
type confifSetter interface{
|
||||||
|
Set()
|
||||||
|
}
|
||||||
|
|
||||||
|
type configGetter interface {
|
||||||
|
Get() int
|
||||||
|
}
|
||||||
|
|
||||||
|
type Mixed interface {
|
||||||
|
confifSetter
|
||||||
|
configGetter
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
type Foo struct {
|
||||||
|
threshold configGetter
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewFoo(new_threshold configGetter) Foo {
|
||||||
|
return Foo{threshold: new_threshold}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f Foo) Bar() {
|
||||||
|
threshold := f.threshold.Get()
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
module main
|
||||||
|
|
||||||
|
go 1.25.5
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"text/template"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
URL = "http://www.omdbapi.com"
|
||||||
|
KEY = "fa22ab55"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SearchRes struct {
|
||||||
|
Title string `json:"Title"`
|
||||||
|
Year string `json:"Year"`
|
||||||
|
Genre string `json:"Genre"`
|
||||||
|
Ratings []Ratings
|
||||||
|
}
|
||||||
|
|
||||||
|
type Ratings struct {
|
||||||
|
Source string `json:"Source"`
|
||||||
|
Value string `json:"Value"`
|
||||||
|
}
|
||||||
|
|
||||||
|
const templ = `
|
||||||
|
=======================
|
||||||
|
Title: {{.Title}}
|
||||||
|
Year: {{.Year}}
|
||||||
|
Genre: {{.Genre}}
|
||||||
|
Rating:
|
||||||
|
{{range .Ratings}}
|
||||||
|
Source: {{.Source}}
|
||||||
|
Rating: {{.Value}}
|
||||||
|
{{end}}
|
||||||
|
=======================
|
||||||
|
`
|
||||||
|
|
||||||
|
var report = template.Must(template.New("MovieReport").Parse(templ))
|
||||||
|
|
||||||
|
func Search(s string) (SearchRes,error) {
|
||||||
|
resp,err := http.Get(URL + "?apikey=" + KEY + "&t=" + s)
|
||||||
|
if err != nil {
|
||||||
|
return SearchRes{},err
|
||||||
|
}
|
||||||
|
|
||||||
|
var res SearchRes
|
||||||
|
|
||||||
|
if err := json.NewDecoder(resp.Body).Decode(&res); err != nil {
|
||||||
|
fmt.Print("Error in unmarshalling ",err)
|
||||||
|
}
|
||||||
|
return res,nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
arg := os.Args[1]
|
||||||
|
// fmt.Println(arg)
|
||||||
|
res,_ := Search(arg)
|
||||||
|
if err := report.Execute(os.Stdout,res); err != nil {
|
||||||
|
fmt.Printf("Cannot show report ",err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
module main
|
||||||
|
|
||||||
|
go 1.24.5
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
module main
|
||||||
|
|
||||||
|
go 1.24.5
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
func launch() {
|
||||||
|
fmt.Println("To abort launch press Enter")
|
||||||
|
for i:=10;i >=0; i-- {
|
||||||
|
fmt.Printf("%d...\n",i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
abort := make(chan struct{})
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
os.Stdin.Read(make([]byte,1))
|
||||||
|
abort <- struct{}{}
|
||||||
|
}()
|
||||||
|
|
||||||
|
tick := time.Tick(1*time.Second)
|
||||||
|
|
||||||
|
for i:=10; i>=0; i-- {
|
||||||
|
fmt.Printf("%d...\n",i)
|
||||||
|
select {
|
||||||
|
case <-tick:
|
||||||
|
case <- abort:
|
||||||
|
fmt.Println("Launch cancelled")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
func kek(i int) {
|
||||||
|
res_string := ""
|
||||||
|
for range i {
|
||||||
|
res_string += " "
|
||||||
|
}
|
||||||
|
fmt.Println(res_string,i)
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
done := make(chan int)
|
||||||
|
|
||||||
|
for i:=range 10 {
|
||||||
|
go func() {
|
||||||
|
kek(i)
|
||||||
|
done <- 1
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
for range 10 {
|
||||||
|
<- done
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
module main
|
||||||
|
|
||||||
|
go 1.24.5
|
||||||
@@ -0,0 +1,133 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type client struct {
|
||||||
|
_chan chan <- string
|
||||||
|
name string
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var (
|
||||||
|
entering = make(chan client)
|
||||||
|
leaving = make(chan client)
|
||||||
|
messages = make(chan string)
|
||||||
|
cancel = make(chan time.Time)
|
||||||
|
)
|
||||||
|
|
||||||
|
func handleConn(conn net.Conn) {
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
|
ch := make(chan string)
|
||||||
|
|
||||||
|
go clientWriter(conn,ch)
|
||||||
|
|
||||||
|
who := conn.RemoteAddr().String()
|
||||||
|
client := client{
|
||||||
|
_chan: ch,
|
||||||
|
name: who,
|
||||||
|
}
|
||||||
|
ch <- "You " + who
|
||||||
|
messages <- who + " connected"
|
||||||
|
entering <- client
|
||||||
|
|
||||||
|
inputChan := make(chan string)
|
||||||
|
timeout := time.NewTimer(40*time.Second)
|
||||||
|
defer timeout.Stop()
|
||||||
|
|
||||||
|
input := bufio.NewScanner(conn)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
log.Println("inside of goroutine")
|
||||||
|
for input.Scan() {
|
||||||
|
log.Println("inside of Scan for loop")
|
||||||
|
inputChan <- client.name + ": " + input.Text()
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := input.Err(); err != nil {
|
||||||
|
log.Printf("Scanner error: %v", err)
|
||||||
|
} else {
|
||||||
|
log.Println("Scanner reached EOF (connection closed)")
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Println("Closing input channel")
|
||||||
|
close(inputChan)
|
||||||
|
}()
|
||||||
|
|
||||||
|
loop:
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case text,ok := <- inputChan:
|
||||||
|
if !ok {
|
||||||
|
leaving <- client
|
||||||
|
messages <- who + " disconnected"
|
||||||
|
break loop
|
||||||
|
}
|
||||||
|
if !timeout.Stop() {
|
||||||
|
<- timeout.C
|
||||||
|
}
|
||||||
|
timeout.Reset(40*time.Second)
|
||||||
|
messages <- text
|
||||||
|
case <- timeout.C:
|
||||||
|
ch <- "Connection timed out due to inactivity"
|
||||||
|
leaving <- client
|
||||||
|
messages <- who + " disconnected (timeout)"
|
||||||
|
break loop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func clientWriter(conn net.Conn, ch <- chan string) {
|
||||||
|
for msg := range ch {
|
||||||
|
fmt.Fprintln(conn,msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func broadcaster() {
|
||||||
|
clients := make(map[client]bool)
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case msg := <- messages:
|
||||||
|
log.Println("Got a msg")
|
||||||
|
for cli := range clients {
|
||||||
|
cli._chan <- msg
|
||||||
|
}
|
||||||
|
case cli := <- entering:
|
||||||
|
cli._chan <- "List of connected clients"
|
||||||
|
for i := range clients {
|
||||||
|
cli._chan <- i.name
|
||||||
|
}
|
||||||
|
clients[cli] = true
|
||||||
|
case cli := <- leaving:
|
||||||
|
log.Printf("Deleting client %s")
|
||||||
|
delete(clients,cli)
|
||||||
|
close(cli._chan)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
listener,err := net.Listen("tcp",":9090")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Error in Lister %v",err)
|
||||||
|
}
|
||||||
|
|
||||||
|
go broadcaster()
|
||||||
|
|
||||||
|
for {
|
||||||
|
conn,err := listener.Accept()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Error in Accept %v",err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
go handleConn(conn)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
module main
|
||||||
|
|
||||||
|
go 1.24.5
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
var k time.Duration = 1
|
||||||
|
for i:=0; i < 100; i++ {
|
||||||
|
k = k<<1
|
||||||
|
time.Sleep(k*time.Second)
|
||||||
|
fmt.Println(k)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func logger(s string) func() {
|
||||||
|
fmt.Printf("Enter in %s\n",s)
|
||||||
|
now := time.Now()
|
||||||
|
return func () {
|
||||||
|
fmt.Printf("Time elapsed %s\n",time.Since(now))
|
||||||
|
fmt.Printf("Exit from func %s\n",s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func SlowOP() {
|
||||||
|
defer logger("SlowOP")()
|
||||||
|
time.Sleep(2*time.Second)
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
SlowOP()
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
module main
|
||||||
|
|
||||||
|
go 1.25.5
|
||||||
Executable
BIN
Binary file not shown.
@@ -0,0 +1,74 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
var verbos = flag.Bool("v",false,"show verbose")
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
flag.Parse()
|
||||||
|
roots := flag.Args()
|
||||||
|
if len(roots) == 0 {
|
||||||
|
roots = []string{"."}
|
||||||
|
}
|
||||||
|
|
||||||
|
fileSizes := make(chan int64)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
for _,root := range roots {
|
||||||
|
WalkDir(root,fileSizes)
|
||||||
|
}
|
||||||
|
close(fileSizes)
|
||||||
|
}()
|
||||||
|
|
||||||
|
var tick <- chan time.Time
|
||||||
|
|
||||||
|
if *verbos{
|
||||||
|
tick = time.Tick(500 * time.Millisecond)
|
||||||
|
}
|
||||||
|
var nfiles,nbytes int64
|
||||||
|
loop:
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case size,ok := <- fileSizes:
|
||||||
|
if !ok {
|
||||||
|
break loop
|
||||||
|
}
|
||||||
|
nfiles++
|
||||||
|
nbytes += size
|
||||||
|
case <- tick:
|
||||||
|
printDU(nfiles,nbytes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func printDU(nfiles, nbytes int64) {
|
||||||
|
fmt.Printf("%d files, %.1f GB\n",nfiles,float64(nbytes)/1e9)
|
||||||
|
}
|
||||||
|
|
||||||
|
func WalkDir(dir string, fileSizes chan <- int64) {
|
||||||
|
for _, entry := range dirents(dir) {
|
||||||
|
if entry.IsDir() {
|
||||||
|
subdir := filepath.Join(dir,entry.Name())
|
||||||
|
WalkDir(subdir,fileSizes)
|
||||||
|
} else {
|
||||||
|
size,_ := entry.Info()
|
||||||
|
fileSizes <- size.Size()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func dirents(dir string) []os.DirEntry{
|
||||||
|
entries, err := os.ReadDir(dir)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr,"du1: %v\n",err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return entries
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
module main
|
||||||
|
|
||||||
|
go 1.25.5
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
type Kek interface {
|
||||||
|
lol()
|
||||||
|
azaza()
|
||||||
|
}
|
||||||
|
|
||||||
|
type Test struct {
|
||||||
|
x,y string
|
||||||
|
}
|
||||||
|
|
||||||
|
func(t Test) lol() {
|
||||||
|
fmt.Printf("I am lol method - %s\n",t.x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func(t Test) azaza() {
|
||||||
|
fmt.Printf("I am azaza method %s\n",t.y)
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ Kek = (&Test{})
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
t := Test{
|
||||||
|
x: "one",
|
||||||
|
y: "two",
|
||||||
|
}
|
||||||
|
t.lol()
|
||||||
|
t.azaza()
|
||||||
|
}
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
package ftp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Conn struct {
|
||||||
|
conn net.Conn
|
||||||
|
dataType dataType
|
||||||
|
dataPort *dataPort
|
||||||
|
rootDir string
|
||||||
|
workDir string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewConn(conn net.Conn, rootDir string) *Conn {
|
||||||
|
return &Conn{
|
||||||
|
conn: conn,
|
||||||
|
rootDir: rootDir,
|
||||||
|
workDir: "/",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Serve (c *Conn) {
|
||||||
|
c.respond(status220)
|
||||||
|
|
||||||
|
s := bufio.NewScanner(c.conn)
|
||||||
|
for s.Scan() {
|
||||||
|
input := strings.Fields(s.Text())
|
||||||
|
if len(input) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
command, args := input[0], input[1:]
|
||||||
|
fmt.Printf("<< %s %v", command, args)
|
||||||
|
|
||||||
|
switch command {
|
||||||
|
case "CWD": // cd
|
||||||
|
c.cwd(args)
|
||||||
|
case "LIST": // ls
|
||||||
|
c.list(args)
|
||||||
|
case "PORT":
|
||||||
|
c.port(args)
|
||||||
|
case "USER":
|
||||||
|
c.user(args)
|
||||||
|
case "QUIT": // close
|
||||||
|
c.respond(status221)
|
||||||
|
return
|
||||||
|
// case "RETR": // get
|
||||||
|
// c.retr(args)
|
||||||
|
case "TYPE":
|
||||||
|
c.setDataType(args)
|
||||||
|
default:
|
||||||
|
c.respond(status502)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if s.Err() != nil {
|
||||||
|
fmt.Print(s.Err())
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package ftp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *Conn) cwd(args []string) {
|
||||||
|
if len(args) != 1 {
|
||||||
|
c.respond(status501)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
workDir := filepath.Join(c.workDir, args[0])
|
||||||
|
absPath := filepath.Join(c.rootDir, workDir)
|
||||||
|
_, err := os.Stat(absPath)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Print(err)
|
||||||
|
c.respond(status550)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.workDir = workDir
|
||||||
|
c.respond(status200)
|
||||||
|
}
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
package ftp
|
||||||
|
|
||||||
|
import "net"
|
||||||
|
|
||||||
|
func (c *Conn) dataConnect() (net.Conn, error) {
|
||||||
|
conn, err := net.Dial("tcp", c.dataPort.toAddress())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return conn, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
dataConn, err = c.dataConnect()
|
||||||
|
if err != nil {
|
||||||
|
log.Print(err)
|
||||||
|
c.respond(status425)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer dataConn.Close()
|
||||||
|
|
||||||
|
for _, file := range files {
|
||||||
|
_, err := fmt.Fprint(dataConn, file.Name(), c.EOL())
|
||||||
|
if err != nil {
|
||||||
|
log.Print(err)
|
||||||
|
c.respond(status426)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_, err = fmt.Fprintf(dataConn, c.EOL())
|
||||||
|
if err != nil {
|
||||||
|
log.Print(err)
|
||||||
|
c.respond(status426)
|
||||||
|
}
|
||||||
|
|
||||||
|
c.respond(status226)
|
||||||
|
|
||||||
|
func (c *Conn) retr(args []string) {
|
||||||
|
if len(args) != 1 {
|
||||||
|
c.respond(status501)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
path := filepath.Join(c.rootDir, c.workDir, args[0])
|
||||||
|
file, err := os.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
log.Print(err)
|
||||||
|
c.respond(status550)
|
||||||
|
}
|
||||||
|
c.respond(status150)
|
||||||
|
|
||||||
|
dataConn, err := c.dataConnect()
|
||||||
|
if err != nil {
|
||||||
|
log.Print(err)
|
||||||
|
c.respond(status425)
|
||||||
|
}
|
||||||
|
defer dataConn.Close()
|
||||||
|
|
||||||
|
_, err = io.Copy(dataConn, file)
|
||||||
|
if err != nil {
|
||||||
|
log.Print(err)
|
||||||
|
c.respond(status426)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
io.WriteString(dataConn, c.EOL())
|
||||||
|
c.respond(status226)
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package ftp
|
||||||
|
|
||||||
|
type dataType int
|
||||||
|
|
||||||
|
const (
|
||||||
|
ascii dataType = iota
|
||||||
|
binary
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *Conn) setDataType(args []string) {
|
||||||
|
if len(args) == 0 {
|
||||||
|
c.respond(status501)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch args[0] {
|
||||||
|
case "A":
|
||||||
|
c.dataType = ascii
|
||||||
|
case "I": // image/binary
|
||||||
|
c.dataType = binary
|
||||||
|
default:
|
||||||
|
c.respond(status504)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.respond(status200)
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
package ftp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *Conn) list(args []string) {
|
||||||
|
var target string
|
||||||
|
if len(args) > 0 {
|
||||||
|
target = filepath.Join(c.rootDir, c.workDir, args[0])
|
||||||
|
} else {
|
||||||
|
target = filepath.Join(c.rootDir, c.workDir)
|
||||||
|
}
|
||||||
|
|
||||||
|
files, err := ioutil.ReadDir(target)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Print(err)
|
||||||
|
c.respond(status550)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.respond(status150)
|
||||||
|
|
||||||
|
dataConn, err := c.dataConnect()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Print(err)
|
||||||
|
c.respond(status425)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer dataConn.Close()
|
||||||
|
|
||||||
|
for _, file := range files {
|
||||||
|
_, err := fmt.Fprint(dataConn, file.Name(), c.EOL())
|
||||||
|
if err != nil {
|
||||||
|
fmt.Print(err)
|
||||||
|
c.respond(status426)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_, err = fmt.Fprintf(dataConn, c.EOL())
|
||||||
|
if err != nil {
|
||||||
|
fmt.Print(err)
|
||||||
|
c.respond(status426)
|
||||||
|
}
|
||||||
|
|
||||||
|
c.respond(status226)
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
package ftp
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
func (c *Conn) port(args []string) {
|
||||||
|
if len(args) != 1 {
|
||||||
|
c.respond(status501)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
dataPort, err := dataPortFromHostPort(args[0])
|
||||||
|
if err != nil {
|
||||||
|
fmt.Print(err)
|
||||||
|
c.respond(status501)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.dataPort = dataPort
|
||||||
|
c.respond(status200)
|
||||||
|
}
|
||||||
|
|
||||||
|
type dataPort struct {
|
||||||
|
h1, h2, h3, h4 int // host
|
||||||
|
p1, p2 int // port
|
||||||
|
}
|
||||||
|
|
||||||
|
func dataPortFromHostPort(hostPort string) (*dataPort, error) {
|
||||||
|
var dp dataPort
|
||||||
|
_, err := fmt.Sscanf(hostPort, "%d,%d,%d,%d,%d,%d",
|
||||||
|
&dp.h1, &dp.h2, &dp.h3, &dp.h4, &dp.p1, &dp.p2)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &dp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *dataPort) toAddress() string {
|
||||||
|
if d == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
// convert hex port bytes to decimal port
|
||||||
|
port := d.p1<<8 + d.p2
|
||||||
|
return fmt.Sprintf("%d.%d.%d.%d:%d", d.h1, d.h2, d.h3, d.h4, port)
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
package ftp
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
const (
|
||||||
|
status150 = "150 File status okay; about to open data connection."
|
||||||
|
status200 = "200 Command okay."
|
||||||
|
status220 = "220 Service ready for new user."
|
||||||
|
status221 = "221 Service closing control connection."
|
||||||
|
status226 = "226 Closing data connection. Requested file action successful."
|
||||||
|
status230 = "230 User %s logged in, proceed."
|
||||||
|
status425 = "425 Can't open data connection."
|
||||||
|
status426 = "426 Connection closed; transfer aborted."
|
||||||
|
status501 = "501 Syntax error in parameters or arguments."
|
||||||
|
status502 = "502 Command not implemented."
|
||||||
|
status504 = "504 Cammand not implemented for that parameter."
|
||||||
|
status550 = "550 Requested action not taken. File unavailable."
|
||||||
|
)
|
||||||
|
|
||||||
|
// respond copies a string to the client and terminates it with the appropriate FTP line terminator
|
||||||
|
// for the datatype.
|
||||||
|
func (c *Conn) respond(s string) {
|
||||||
|
fmt.Print(">> ", s)
|
||||||
|
_, err := fmt.Fprint(c.conn, s, c.EOL())
|
||||||
|
if err != nil {
|
||||||
|
fmt.Print(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// EOL returns the line terminator matching the FTP standard for the datatype.
|
||||||
|
func (c *Conn) EOL() string {
|
||||||
|
switch c.dataType {
|
||||||
|
case ascii:
|
||||||
|
return "\r\n"
|
||||||
|
case binary:
|
||||||
|
return "\n"
|
||||||
|
default:
|
||||||
|
return "\n"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package ftp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *Conn) user(args []string) {
|
||||||
|
c.respond(fmt.Sprintf(status230, strings.Join(args, " ")))
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
module main
|
||||||
|
|
||||||
|
go 1.24.5
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
"path/filepath"
|
||||||
|
"main/ftp"
|
||||||
|
)
|
||||||
|
|
||||||
|
var port int
|
||||||
|
var rootDir string
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
flag.IntVar(&port,"port",9090,"port to listet")
|
||||||
|
flag.StringVar(&rootDir,"rootDir","public","root Directory")
|
||||||
|
flag.Parse()
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleConn(conn net.Conn) {
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
|
absPath, err := filepath.Abs(rootDir)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Print(err)
|
||||||
|
}
|
||||||
|
ftp.Serve(ftp.NewConn(conn,absPath))
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
server := fmt.Sprintf(":%d",port)
|
||||||
|
listener,err := net.Listen("tcp",server)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
for {
|
||||||
|
conn, err := listener.Accept()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Print(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
handleConn(conn)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
module main
|
||||||
|
|
||||||
|
go 1.25.5
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
func findSub(s, sub, repl string) (finStr string) {
|
||||||
|
if len(s) < len(sub) {
|
||||||
|
return s[:]
|
||||||
|
}
|
||||||
|
|
||||||
|
found := false
|
||||||
|
currCount := 0
|
||||||
|
newStr := ""
|
||||||
|
|
||||||
|
if s[:len(sub)] == sub {
|
||||||
|
newStr = repl
|
||||||
|
currCount++
|
||||||
|
found = true
|
||||||
|
// return
|
||||||
|
} else {
|
||||||
|
newStr = string(s[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
var resStr string
|
||||||
|
|
||||||
|
if found {
|
||||||
|
resStr = findSub(s[len(sub):], sub, repl)
|
||||||
|
} else {
|
||||||
|
resStr = findSub(s[1:],sub,repl)
|
||||||
|
}
|
||||||
|
|
||||||
|
return newStr + resStr
|
||||||
|
}
|
||||||
|
|
||||||
|
func expand(s,sub,repl string, f func(init string,sub string,repl string) string) string{
|
||||||
|
res := f(s,sub,repl)
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
str := flag.String("str", "lolkeklolkeklol", "Initital string")
|
||||||
|
sub := flag.String("sub", "kek", "substring to find")
|
||||||
|
repl := flag.String("repl", "puk", "String to replace with")
|
||||||
|
|
||||||
|
// res := findSub(*str, *sub, *repl)
|
||||||
|
res := expand(*str,*sub,*repl,findSub)
|
||||||
|
fmt.Println(res)
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
module main
|
||||||
|
|
||||||
|
go 1.25.5
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type newWriter struct {
|
||||||
|
conn io.Writer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (nw newWriter) Write(p []byte) (n int, err error) {
|
||||||
|
prefix := []byte("curr time is ")
|
||||||
|
prefix = append(prefix, p...)
|
||||||
|
n, err = nw.conn.Write(prefix)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleConn(conn net.Conn) {
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
|
for {
|
||||||
|
_, err := io.WriteString(newWriter{conn}, time.Now().Format("15:04:05\n"))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
listener, err := net.Listen("tcp", ":9090")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Shit happened %s",err)
|
||||||
|
}
|
||||||
|
for {
|
||||||
|
conn, err := listener.Accept()
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Error is %s",err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
go handleConn(conn)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
module main
|
||||||
|
|
||||||
|
go 1.25.5
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
for {
|
||||||
|
for _,r := range `-\|/` {
|
||||||
|
fmt.Printf("\r processing in progress %c",r)
|
||||||
|
time.Sleep(50*time.Millisecond)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
module main
|
||||||
|
|
||||||
|
go 1.25.5
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
"html/template"
|
||||||
|
)
|
||||||
|
|
||||||
|
type rub float64
|
||||||
|
|
||||||
|
func (r rub) String() string {
|
||||||
|
return fmt.Sprintf("%.2f",r)
|
||||||
|
}
|
||||||
|
|
||||||
|
var tmpl *template.Template
|
||||||
|
|
||||||
|
|
||||||
|
func Render(w http.ResponseWriter, req *http.Request) {
|
||||||
|
err := tmpl.Execute(w,req)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w,err.Error(),http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type database map[string]rub
|
||||||
|
|
||||||
|
type List struct {
|
||||||
|
Items database
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *database) list(w http.ResponseWriter, req *http.Request) {
|
||||||
|
data := List{
|
||||||
|
Items: *db,
|
||||||
|
}
|
||||||
|
|
||||||
|
err := tmpl.ExecuteTemplate(w,"template.html",data)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w,err.Error(),http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *database) price(w http.ResponseWriter,req *http.Request) {
|
||||||
|
item := req.URL.Query().Get("item")
|
||||||
|
v,ok := (*db)[item]
|
||||||
|
if !ok {
|
||||||
|
w.WriteHeader(http.StatusNotFound)
|
||||||
|
fmt.Fprintf(w,"%s not found in goods list",item)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fmt.Fprintf(w,"Price of %s is %s",item,v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *database) update(w http.ResponseWriter, req *http.Request) {
|
||||||
|
item := req.URL.Query().Get("item")
|
||||||
|
price := req.URL.Query().Get("price")
|
||||||
|
f,err := strconv.ParseFloat(price,2)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
(*db)[item] = rub(f)
|
||||||
|
fmt.Fprintf(w,"item %s and price %s",item,(*db)[item])
|
||||||
|
}
|
||||||
|
|
||||||
|
// func (db database) ServeHTTP(w http.ResponseWriter, r *http.Request ) {
|
||||||
|
// switch r.URL.Path {
|
||||||
|
// case "/list":
|
||||||
|
// for k,v := range db {
|
||||||
|
// fmt.Fprintf(w,"%s costs %s rub\n",k,v)
|
||||||
|
// }
|
||||||
|
// case "/price":
|
||||||
|
// item := r.URL.Query().Get("item")
|
||||||
|
// v,ok := db[item]
|
||||||
|
// if !ok {
|
||||||
|
// w.WriteHeader(http.StatusNotFound)
|
||||||
|
// fmt.Fprintf(w,"%s not found in goods list",item)
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
// fmt.Fprintf(w,"Price of %s is %s",item,v)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
db := database{
|
||||||
|
"sneakers": 10.0,
|
||||||
|
"boots": 20,
|
||||||
|
}
|
||||||
|
// mux.Handle("/list",http.HandlerFunc(db.list))
|
||||||
|
// mux.Handle("/price",http.HandlerFunc(db.price))
|
||||||
|
tmpl = template.Must(template.ParseFiles("template.html"))
|
||||||
|
http.HandleFunc("/list",db.list)
|
||||||
|
http.HandleFunc("/price",db.price)
|
||||||
|
http.HandleFunc("/update",db.update)
|
||||||
|
log.Fatal(http.ListenAndServe(":9090",nil))
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>TABLE</title>
|
||||||
|
<style>
|
||||||
|
body { font-family: Arial, sans-serif; margin: 40px; }
|
||||||
|
.header { background: #3498db; color: white; padding: 20px; border-radius: 8px; }
|
||||||
|
.content { margin: 20px 0; }
|
||||||
|
table { width: 100%; border-collapse: collapse; }
|
||||||
|
th, td { padding: 10px; border: 1px solid #ddd; text-align: left; }
|
||||||
|
th { background: #f4f4f4; }
|
||||||
|
.price { text-align: right; font-weight: bold; color: #27ae60; }
|
||||||
|
.total { background: #ecf0f1; font-weight: bold; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div class="content">
|
||||||
|
<h2>Items</h2>
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<th>Item Name</th>
|
||||||
|
<th>Price</th>
|
||||||
|
</tr>
|
||||||
|
{{range $key, $value := .Items}}
|
||||||
|
<tr>
|
||||||
|
<td>{{$key}}</td>
|
||||||
|
<td class="price">{{$value.String}}</td>
|
||||||
|
</tr>
|
||||||
|
{{end}}
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
module main
|
||||||
|
|
||||||
|
go 1.25.5
|
||||||
@@ -0,0 +1,123 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ByteCount int
|
||||||
|
|
||||||
|
func (bc *ByteCount) Write(p []byte) (n int,err error) {
|
||||||
|
*bc = ByteCount(len(p))
|
||||||
|
return len(p),nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type WordsNum int
|
||||||
|
|
||||||
|
func (wn *WordsNum) Write(p []byte) (n int,err error) {
|
||||||
|
*wn = WordsNum(len(p))
|
||||||
|
return len(p),nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func Count() []byte {
|
||||||
|
scanner := bufio.NewScanner(os.Stdin)
|
||||||
|
|
||||||
|
fmt.Print("Enter text here: ")
|
||||||
|
if scanner.Scan() {
|
||||||
|
line := scanner.Text()
|
||||||
|
|
||||||
|
wordCount := bufio.NewScanner(strings.NewReader(line))
|
||||||
|
wordCount.Split(bufio.ScanWords)
|
||||||
|
|
||||||
|
wordsNum := []byte{}
|
||||||
|
for wordCount.Scan() {
|
||||||
|
wordsNum = append(wordsNum, 1)
|
||||||
|
fmt.Println("Word is: ",wordCount.Text())
|
||||||
|
}
|
||||||
|
fmt.Printf("Amount of words is %d\n",wordsNum)
|
||||||
|
return wordsNum
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type NewWriter struct {
|
||||||
|
writer io.Writer
|
||||||
|
counter *int64
|
||||||
|
}
|
||||||
|
|
||||||
|
func (nw *NewWriter) Write(p []byte) (n int,err error) {
|
||||||
|
n,err = nw.writer.Write(p)
|
||||||
|
fmt.Println()
|
||||||
|
*nw.counter = int64(n)
|
||||||
|
return n,err
|
||||||
|
}
|
||||||
|
|
||||||
|
func CountingWriter(w io.Writer) (io.Writer, *int64) {
|
||||||
|
counter := int64(0)
|
||||||
|
return &NewWriter{
|
||||||
|
writer: w,
|
||||||
|
counter: &counter,
|
||||||
|
},&counter
|
||||||
|
}
|
||||||
|
|
||||||
|
// Упражнения на возврат LimitReader
|
||||||
|
func GetNewReader(s string) io.Reader {
|
||||||
|
return strings.NewReader(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
type MyReader struct {
|
||||||
|
reader io.Reader
|
||||||
|
constraint int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (nr MyReader) Read(p []byte) (n int,err error) {
|
||||||
|
n,err = nr.reader.Read(p[:nr.constraint])
|
||||||
|
return
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func LimitReader(r io.Reader,n int) io.Reader {
|
||||||
|
return MyReader{
|
||||||
|
reader: r,
|
||||||
|
constraint: n,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Проверка, что ByteCount действительно соответствует интерфейсу io.Writer
|
||||||
|
var _ io.Writer = new(ByteCount)
|
||||||
|
|
||||||
|
var period = flag.Duration("period",1 * time.Second,"sleep period")
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
flag.Parse()
|
||||||
|
fmt.Printf("Waiting %v...",period)
|
||||||
|
time.Sleep(*period)
|
||||||
|
fmt.Println()
|
||||||
|
// var bc ByteCount
|
||||||
|
// bc.Write([]byte("Hello dodik"))
|
||||||
|
// fmt.Println(bc)
|
||||||
|
|
||||||
|
// var wn WordsNum
|
||||||
|
// wn.Write(Count())
|
||||||
|
// fmt.Println(wn)
|
||||||
|
|
||||||
|
// writer,count := CountingWriter(os.Stdout)
|
||||||
|
// writer.Write([]byte("Hello Baga"))
|
||||||
|
// fmt.Printf("Bytes %d",*count)
|
||||||
|
|
||||||
|
// str := "hello"
|
||||||
|
// buf := make([]byte,100)
|
||||||
|
// reader := LimitReader(strings.NewReader(str),3)
|
||||||
|
// // n,_ := reader.Read(buf)
|
||||||
|
// // reader := strings.NewReader(str)
|
||||||
|
// n,_ := reader.Read(buf)
|
||||||
|
// fmt.Printf("Result is %d\n",n)
|
||||||
|
// fmt.Printf("String is %s",string(buf))
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
module main
|
||||||
|
|
||||||
|
go 1.25.5
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
func mustCopy(dst io.Writer,src io.Reader) {
|
||||||
|
if _,err := io.Copy(dst,src); err != nil {
|
||||||
|
log.Fatalf("Error in mustCopy %s",err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
conn,err := net.Dial("tcp",":9090")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Error in Dial %s",err)
|
||||||
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
done := make(chan struct{})
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
io.Copy(os.Stdin,conn)
|
||||||
|
fmt.Println("done")
|
||||||
|
done <- struct{}{}
|
||||||
|
}()
|
||||||
|
io.Copy(conn,os.Stdout)
|
||||||
|
conn.Close()
|
||||||
|
<-done
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
module main
|
||||||
|
|
||||||
|
go 1.25.5
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func echo(c net.Conn, shout string, t time.Duration) {
|
||||||
|
for _ = range 3 {
|
||||||
|
fmt.Fprintln(c, "\t", strings.ToUpper(shout))
|
||||||
|
time.Sleep(t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleConn(conn net.Conn) {
|
||||||
|
|
||||||
|
count := 1
|
||||||
|
defer fmt.Println("CLosed Connection")
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
|
fmt.Println("Accepted connection")
|
||||||
|
|
||||||
|
ticker := time.NewTicker(2*time.Second)
|
||||||
|
|
||||||
|
input := bufio.NewScanner(conn)
|
||||||
|
select {
|
||||||
|
case <- ticker.C:
|
||||||
|
fmt.Println("TIMEOUT")
|
||||||
|
default:
|
||||||
|
fmt.Println("DEFAULT")
|
||||||
|
}
|
||||||
|
|
||||||
|
for input.Scan() {
|
||||||
|
fmt.Println("TICK")
|
||||||
|
switch input.Text() {
|
||||||
|
case "info":
|
||||||
|
fmt.Printf("INFO %d",count)
|
||||||
|
default:
|
||||||
|
count++
|
||||||
|
go func() {
|
||||||
|
defer func (){
|
||||||
|
count--
|
||||||
|
}()
|
||||||
|
echo(conn, input.Text(), 1*time.Second)
|
||||||
|
}()
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
listener, err := net.Listen("tcp", ":9090")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Error %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
conn, err := listener.Accept()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Error in Accept %s", err)
|
||||||
|
}
|
||||||
|
go handleConn(conn)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
count := 1
|
||||||
|
side1 := make(chan int)
|
||||||
|
side2 := make(chan int)
|
||||||
|
|
||||||
|
tick := time.NewTicker(1*time.Second)
|
||||||
|
|
||||||
|
go func(){
|
||||||
|
for range side1 {
|
||||||
|
fmt.Println("Tick")
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
for range side2 {
|
||||||
|
fmt.Println("Tack")
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
for range tick.C {
|
||||||
|
if count % 2 == 0 {
|
||||||
|
side2 <- 1
|
||||||
|
count = 1
|
||||||
|
} else {
|
||||||
|
side1 <- 1
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
module main
|
||||||
|
|
||||||
|
go 1.25.5
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"html/template"
|
||||||
|
// "log"
|
||||||
|
"os"
|
||||||
|
"sort"
|
||||||
|
"text/tabwriter"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Track struct {
|
||||||
|
Title string
|
||||||
|
Artist string
|
||||||
|
Album string
|
||||||
|
Year int
|
||||||
|
Length time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
var tracks = []*Track{
|
||||||
|
{"Go", "Delilah", "From the Roots Up", 2012, length("3m38s")},
|
||||||
|
{"Go", "Moby", "Moby", 1992, length("3m37s")},
|
||||||
|
{"Go Ahead", "Alicia Keys", "As I Am", 2007, length("4m36s")},
|
||||||
|
{"Ready 2 Go", "Martin Solveig", "Smash", 2011, length("4m24s")},
|
||||||
|
}
|
||||||
|
|
||||||
|
var issueList = template.Must(template.New("tracklist").Parse(`
|
||||||
|
<table>
|
||||||
|
<tr style='text-align: left'>
|
||||||
|
<th>#</th>
|
||||||
|
<th>Title</th>
|
||||||
|
<th>Artist</th>
|
||||||
|
<th>Album</th>
|
||||||
|
</tr>
|
||||||
|
{{range .}}
|
||||||
|
<tr>
|
||||||
|
<td>{{.Title}}</td>
|
||||||
|
<td>{{.Artist}}</td>
|
||||||
|
<td>{{.Album}}</td>
|
||||||
|
</tr>
|
||||||
|
{{end}}
|
||||||
|
</table>
|
||||||
|
`))
|
||||||
|
|
||||||
|
|
||||||
|
func length(s string) time.Duration {
|
||||||
|
d, err := time.ParseDuration(s)
|
||||||
|
if err != nil {
|
||||||
|
panic(s)
|
||||||
|
}
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
|
||||||
|
func PrintTracks(tracks []*Track) {
|
||||||
|
const format = "%v\t%v\t%v\t%v\t%v\n"
|
||||||
|
tw := tabwriter.NewWriter(os.Stdout,0,8,2,' ',0)
|
||||||
|
fmt.Fprintf(tw,format,"Title","Artist","Album","Year","Length")
|
||||||
|
fmt.Fprintf(tw, format, "-----", "------", "-----", "----", "------")
|
||||||
|
for _, t := range tracks {
|
||||||
|
fmt.Fprintf(tw,format,t.Title,t.Artist,t.Album,t.Year,t.Length)
|
||||||
|
}
|
||||||
|
tw.Flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
type byArtists []*Track
|
||||||
|
|
||||||
|
func (x byArtists) Len() int {return len(x)}
|
||||||
|
func (x byArtists) Less(i,j int) bool {return x[i].Artist < x[j].Artist}
|
||||||
|
func (x byArtists) Swap(i,j int) { x[i],x[j] = x[j],x[i]}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
PrintTracks(tracks)
|
||||||
|
fmt.Println()
|
||||||
|
sort.Sort(byArtists(tracks))
|
||||||
|
PrintTracks(tracks)
|
||||||
|
// if err := issueList.Execute(os.Stdout,tracks); err != nil {
|
||||||
|
// log.Fatal(err)
|
||||||
|
// }
|
||||||
|
}
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
module proj1
|
module proj1
|
||||||
|
|
||||||
go 1.24.5
|
go 1.24.5
|
||||||
|
|
||||||
|
require go.uber.org/zap v1.27.1
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc=
|
||||||
|
go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
||||||
+49
-4
@@ -1,14 +1,59 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
// "strings"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const URL = "https://api.github.com/search/issues"
|
||||||
|
|
||||||
|
type IssuerSearchReport struct {
|
||||||
|
Total int `json:"total_count"`
|
||||||
|
Incomplete_results bool `json:"incomplete_results"`
|
||||||
|
Created_at time.Time `json:"created_at"`
|
||||||
|
Items []Issue
|
||||||
|
}
|
||||||
|
|
||||||
|
type Issue struct {
|
||||||
|
Title string `json:"title"`
|
||||||
|
User User
|
||||||
|
}
|
||||||
|
|
||||||
|
type User struct {
|
||||||
|
Login string `json:"login"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func IssuerSearch(s []string) (IssuerSearchReport,error) {
|
||||||
|
new_s := s[0]
|
||||||
|
resp,err := http.Get(URL + "?q=" + new_s)
|
||||||
|
if err != nil {
|
||||||
|
return IssuerSearchReport{},err
|
||||||
|
}
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
resp.Body.Close()
|
||||||
|
return IssuerSearchReport{},fmt.Errorf("Error in request",resp.Status)
|
||||||
|
}
|
||||||
|
|
||||||
|
var result IssuerSearchReport
|
||||||
|
|
||||||
|
body,_ := io.ReadAll(resp.Body)
|
||||||
|
if err := json.Unmarshal(body,&result); err != nil {
|
||||||
|
resp.Body.Close()
|
||||||
|
return IssuerSearchReport{},nil
|
||||||
|
}
|
||||||
|
resp.Body.Close()
|
||||||
|
return result,nil
|
||||||
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
// var sum string
|
result,err := IssuerSearch(os.Args[1:])
|
||||||
for key,val := range os.Args[:]{
|
if err != nil {
|
||||||
fmt.Println("num is",key,"value is", val)
|
fmt.Println("Error in IssuerSearch")
|
||||||
}
|
}
|
||||||
|
fmt.Printf("%#v\n",result)
|
||||||
|
fmt.Println(result)
|
||||||
}
|
}
|
||||||
Regular → Executable
+6
-14
@@ -1,20 +1,12 @@
|
|||||||
FROM golang:1.25-alpine AS builder
|
|
||||||
|
|
||||||
WORKDIR /app
|
|
||||||
COPY go.mod go.sum ./
|
|
||||||
RUN go mod download
|
|
||||||
|
|
||||||
COPY . .
|
|
||||||
RUN CGO_ENABLED=0 GOOS=linux go build -o exporter .
|
|
||||||
|
|
||||||
FROM ubuntu:22.04
|
FROM ubuntu:22.04
|
||||||
RUN apt-get update && apt-get install -y \
|
RUN apt-get update && apt-get install -y \
|
||||||
ceph-common \
|
librbd-dev \
|
||||||
|
librados-dev \
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
WORKDIR /home/
|
RUN mkdir /app
|
||||||
|
WORKDIR /app
|
||||||
COPY --from=builder /app/exporter .
|
COPY ./rbd-exporter /app/rbd-exporter
|
||||||
EXPOSE 9040
|
EXPOSE 9040
|
||||||
|
|
||||||
CMD [ "./exporter" ]
|
CMD [ "./rbd-exporter" ]
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
.PHONY: test test-unit test-integration test-coverage
|
||||||
|
|
||||||
|
test-unit:
|
||||||
|
go test -v -short ./...
|
||||||
|
|
||||||
|
test-integration:
|
||||||
|
go test -v -tags=integration ./...
|
||||||
|
|
||||||
|
test-coverage:
|
||||||
|
go test -v -coverprofile=coverage.out ./...
|
||||||
|
go tool cover -html=coverage.out -o coverage.html
|
||||||
|
|
||||||
|
test: test-unit
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f coverage.out coverage.html
|
||||||
|
|
||||||
|
total-rm:
|
||||||
|
rm -f main_test.go metrics_test.go pool_test.go types_test.go integration_test.go mock.go
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
func PoolFactory(cephConn CephConnector, poolName string) (Pool, error) {
|
||||||
|
var rbdlist []iRBD = []iRBD{}
|
||||||
|
|
||||||
|
ioctx, err := cephConn.OpenIOContext(poolName)
|
||||||
|
if err != nil {
|
||||||
|
return Pool{}, fmt.Errorf("Couldn't set context for pool %s %w", poolName, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer ioctx.Destroy()
|
||||||
|
|
||||||
|
imageList, err := ioctx.GetImageNames()
|
||||||
|
if err != nil {
|
||||||
|
return Pool{}, fmt.Errorf("Couldn't get list of rbds %w", err)
|
||||||
|
}
|
||||||
|
for _, rbdname := range imageList {
|
||||||
|
stat, err := RBDFacroty(ioctx, rbdname)
|
||||||
|
if err != nil {
|
||||||
|
logger.Errorf("Coundn't get stat from disk %s %w", rbdname, err)
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
rbdlist = append(rbdlist, stat)
|
||||||
|
}
|
||||||
|
return Pool{
|
||||||
|
Name: poolName,
|
||||||
|
HasRBD: len(imageList) != 0,
|
||||||
|
RBDlist: rbdlist,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func RBDFacroty(ioctx IOContexter, rbdname string) (RBD, error) {
|
||||||
|
defer func() {
|
||||||
|
if v := recover(); v != nil {
|
||||||
|
fmt.Println("Shit happened in RBDFactory")
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
image := ioctx.GetImage(rbdname)
|
||||||
|
err := image.Open()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer image.Close()
|
||||||
|
|
||||||
|
|
||||||
|
info,err := image.Stat()
|
||||||
|
if err != nil {
|
||||||
|
return RBD{},fmt.Errorf("Couldn't get stats for image %s %w",rbdname,err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return RBD{
|
||||||
|
Name: rbdname,
|
||||||
|
ImageInfo: *info,
|
||||||
|
},nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,255 @@
|
|||||||
|
// Code generated by MockGen. DO NOT EDIT.
|
||||||
|
// Source: types.go
|
||||||
|
//
|
||||||
|
// Generated by this command:
|
||||||
|
//
|
||||||
|
// mockgen -source=types.go -destination=RBDFactory_mockgen_mock.go -package=main
|
||||||
|
//
|
||||||
|
|
||||||
|
// Package main is a generated GoMock package.
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
reflect "reflect"
|
||||||
|
|
||||||
|
rbd "github.com/ceph/go-ceph/rbd"
|
||||||
|
gomock "go.uber.org/mock/gomock"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MockCephConnector is a mock of CephConnector interface.
|
||||||
|
type MockCephConnector struct {
|
||||||
|
ctrl *gomock.Controller
|
||||||
|
recorder *MockCephConnectorMockRecorder
|
||||||
|
isgomock struct{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MockCephConnectorMockRecorder is the mock recorder for MockCephConnector.
|
||||||
|
type MockCephConnectorMockRecorder struct {
|
||||||
|
mock *MockCephConnector
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMockCephConnector creates a new mock instance.
|
||||||
|
func NewMockCephConnector(ctrl *gomock.Controller) *MockCephConnector {
|
||||||
|
mock := &MockCephConnector{ctrl: ctrl}
|
||||||
|
mock.recorder = &MockCephConnectorMockRecorder{mock}
|
||||||
|
return mock
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXPECT returns an object that allows the caller to indicate expected use.
|
||||||
|
func (m *MockCephConnector) EXPECT() *MockCephConnectorMockRecorder {
|
||||||
|
return m.recorder
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListPools mocks base method.
|
||||||
|
func (m *MockCephConnector) ListPools() ([]string, error) {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "ListPools")
|
||||||
|
ret0, _ := ret[0].([]string)
|
||||||
|
ret1, _ := ret[1].(error)
|
||||||
|
return ret0, ret1
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListPools indicates an expected call of ListPools.
|
||||||
|
func (mr *MockCephConnectorMockRecorder) ListPools() *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListPools", reflect.TypeOf((*MockCephConnector)(nil).ListPools))
|
||||||
|
}
|
||||||
|
|
||||||
|
// OpenIOContext mocks base method.
|
||||||
|
func (m *MockCephConnector) OpenIOContext(poolName string) (IOContexter, error) {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "OpenIOContext", poolName)
|
||||||
|
ret0, _ := ret[0].(IOContexter)
|
||||||
|
ret1, _ := ret[1].(error)
|
||||||
|
return ret0, ret1
|
||||||
|
}
|
||||||
|
|
||||||
|
// OpenIOContext indicates an expected call of OpenIOContext.
|
||||||
|
func (mr *MockCephConnectorMockRecorder) OpenIOContext(poolName any) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OpenIOContext", reflect.TypeOf((*MockCephConnector)(nil).OpenIOContext), poolName)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MockIOContexter is a mock of IOContexter interface.
|
||||||
|
type MockIOContexter struct {
|
||||||
|
ctrl *gomock.Controller
|
||||||
|
recorder *MockIOContexterMockRecorder
|
||||||
|
isgomock struct{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MockIOContexterMockRecorder is the mock recorder for MockIOContexter.
|
||||||
|
type MockIOContexterMockRecorder struct {
|
||||||
|
mock *MockIOContexter
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMockIOContexter creates a new mock instance.
|
||||||
|
func NewMockIOContexter(ctrl *gomock.Controller) *MockIOContexter {
|
||||||
|
mock := &MockIOContexter{ctrl: ctrl}
|
||||||
|
mock.recorder = &MockIOContexterMockRecorder{mock}
|
||||||
|
return mock
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXPECT returns an object that allows the caller to indicate expected use.
|
||||||
|
func (m *MockIOContexter) EXPECT() *MockIOContexterMockRecorder {
|
||||||
|
return m.recorder
|
||||||
|
}
|
||||||
|
|
||||||
|
// Destroy mocks base method.
|
||||||
|
func (m *MockIOContexter) Destroy() {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
m.ctrl.Call(m, "Destroy")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Destroy indicates an expected call of Destroy.
|
||||||
|
func (mr *MockIOContexterMockRecorder) Destroy() *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Destroy", reflect.TypeOf((*MockIOContexter)(nil).Destroy))
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetImage mocks base method.
|
||||||
|
func (m *MockIOContexter) GetImage(name string) RBDImager {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "GetImage", name)
|
||||||
|
ret0, _ := ret[0].(RBDImager)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetImage indicates an expected call of GetImage.
|
||||||
|
func (mr *MockIOContexterMockRecorder) GetImage(name any) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetImage", reflect.TypeOf((*MockIOContexter)(nil).GetImage), name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetImageNames mocks base method.
|
||||||
|
func (m *MockIOContexter) GetImageNames() ([]string, error) {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "GetImageNames")
|
||||||
|
ret0, _ := ret[0].([]string)
|
||||||
|
ret1, _ := ret[1].(error)
|
||||||
|
return ret0, ret1
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetImageNames indicates an expected call of GetImageNames.
|
||||||
|
func (mr *MockIOContexterMockRecorder) GetImageNames() *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetImageNames", reflect.TypeOf((*MockIOContexter)(nil).GetImageNames))
|
||||||
|
}
|
||||||
|
|
||||||
|
// MockRBDImager is a mock of RBDImager interface.
|
||||||
|
type MockRBDImager struct {
|
||||||
|
ctrl *gomock.Controller
|
||||||
|
recorder *MockRBDImagerMockRecorder
|
||||||
|
isgomock struct{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MockRBDImagerMockRecorder is the mock recorder for MockRBDImager.
|
||||||
|
type MockRBDImagerMockRecorder struct {
|
||||||
|
mock *MockRBDImager
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMockRBDImager creates a new mock instance.
|
||||||
|
func NewMockRBDImager(ctrl *gomock.Controller) *MockRBDImager {
|
||||||
|
mock := &MockRBDImager{ctrl: ctrl}
|
||||||
|
mock.recorder = &MockRBDImagerMockRecorder{mock}
|
||||||
|
return mock
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXPECT returns an object that allows the caller to indicate expected use.
|
||||||
|
func (m *MockRBDImager) EXPECT() *MockRBDImagerMockRecorder {
|
||||||
|
return m.recorder
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close mocks base method.
|
||||||
|
func (m *MockRBDImager) Close() error {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "Close")
|
||||||
|
ret0, _ := ret[0].(error)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close indicates an expected call of Close.
|
||||||
|
func (mr *MockRBDImagerMockRecorder) Close() *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockRBDImager)(nil).Close))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open mocks base method.
|
||||||
|
func (m *MockRBDImager) Open() error {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "Open")
|
||||||
|
ret0, _ := ret[0].(error)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open indicates an expected call of Open.
|
||||||
|
func (mr *MockRBDImagerMockRecorder) Open() *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Open", reflect.TypeOf((*MockRBDImager)(nil).Open))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stat mocks base method.
|
||||||
|
func (m *MockRBDImager) Stat() (*rbd.ImageInfo, error) {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "Stat")
|
||||||
|
ret0, _ := ret[0].(*rbd.ImageInfo)
|
||||||
|
ret1, _ := ret[1].(error)
|
||||||
|
return ret0, ret1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stat indicates an expected call of Stat.
|
||||||
|
func (mr *MockRBDImagerMockRecorder) Stat() *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Stat", reflect.TypeOf((*MockRBDImager)(nil).Stat))
|
||||||
|
}
|
||||||
|
|
||||||
|
// MockiRBD is a mock of iRBD interface.
|
||||||
|
type MockiRBD struct {
|
||||||
|
ctrl *gomock.Controller
|
||||||
|
recorder *MockiRBDMockRecorder
|
||||||
|
isgomock struct{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MockiRBDMockRecorder is the mock recorder for MockiRBD.
|
||||||
|
type MockiRBDMockRecorder struct {
|
||||||
|
mock *MockiRBD
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMockiRBD creates a new mock instance.
|
||||||
|
func NewMockiRBD(ctrl *gomock.Controller) *MockiRBD {
|
||||||
|
mock := &MockiRBD{ctrl: ctrl}
|
||||||
|
mock.recorder = &MockiRBDMockRecorder{mock}
|
||||||
|
return mock
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXPECT returns an object that allows the caller to indicate expected use.
|
||||||
|
func (m *MockiRBD) EXPECT() *MockiRBDMockRecorder {
|
||||||
|
return m.recorder
|
||||||
|
}
|
||||||
|
|
||||||
|
// getName mocks base method.
|
||||||
|
func (m *MockiRBD) getName() string {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "getName")
|
||||||
|
ret0, _ := ret[0].(string)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// getName indicates an expected call of getName.
|
||||||
|
func (mr *MockiRBDMockRecorder) getName() *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "getName", reflect.TypeOf((*MockiRBD)(nil).getName))
|
||||||
|
}
|
||||||
|
|
||||||
|
// getSize mocks base method.
|
||||||
|
func (m *MockiRBD) getSize() int64 {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "getSize")
|
||||||
|
ret0, _ := ret[0].(int64)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// getSize indicates an expected call of getSize.
|
||||||
|
func (mr *MockiRBDMockRecorder) getSize() *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "getSize", reflect.TypeOf((*MockiRBD)(nil).getSize))
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/ceph/go-ceph/rados"
|
||||||
|
)
|
||||||
|
|
||||||
|
func connect() (_ CephConnection, err error) {
|
||||||
|
var cephConn CephConnection = CephConnection{}
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("Error in func connect() %w", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
cephConn.conn, err = rados.NewConnWithClusterAndUser("ceph", "client.admin")
|
||||||
|
if err != nil {
|
||||||
|
return CephConnection{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = cephConn.conn.ReadConfigFile(params.Config)
|
||||||
|
if err != nil {
|
||||||
|
return CephConnection{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = cephConn.conn.SetConfigOption("keyring", params.Keyring)
|
||||||
|
if err != nil {
|
||||||
|
return CephConnection{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = cephConn.conn.Connect()
|
||||||
|
if err != nil {
|
||||||
|
return CephConnection{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return cephConn, nil
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
mode: set
|
||||||
@@ -21,7 +21,7 @@ spec:
|
|||||||
spec:
|
spec:
|
||||||
containers:
|
containers:
|
||||||
- name: export
|
- name: export
|
||||||
image: serviceplant/goexp:0.0.28
|
image: serviceplant/goexp:0.0.31
|
||||||
ports:
|
ports:
|
||||||
- containerPort: 9040
|
- containerPort: 9040
|
||||||
name: metrics
|
name: metrics
|
||||||
@@ -91,7 +91,7 @@ spec:
|
|||||||
write_endpoints
|
write_endpoints
|
||||||
|
|
||||||
# continuously update the mon endpoints if they fail over
|
# continuously update the mon endpoints if they fail over
|
||||||
exec /home/exporter &
|
exec /app/rbd-exporter --keyring /etc/ceph/keyring &
|
||||||
watch_endpoints
|
watch_endpoints
|
||||||
imagePullPolicy: IfNotPresent
|
imagePullPolicy: IfNotPresent
|
||||||
tty: true
|
tty: true
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
"go.uber.org/zap/zapcore"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// This func runs even before main()
|
||||||
|
func init() {
|
||||||
|
|
||||||
|
config_file := flag.String("config", "/etc/ceph/ceph.conf", "placement of ceph config file")
|
||||||
|
_keyring := flag.String("keyring", "/etc/ceph/ceph.client.admin.keyring", "placement of ceph keyring file")
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
params.Config = *config_file
|
||||||
|
params.Keyring = *_keyring
|
||||||
|
|
||||||
|
logger = loggerInit()
|
||||||
|
logger.Info("Setting up logger is complete successfully")
|
||||||
|
logger.Info("Registering prom metrics")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Here I initialize logger and set some custom settings
|
||||||
|
func loggerInit() *zap.SugaredLogger {
|
||||||
|
config := zap.NewDevelopmentConfig()
|
||||||
|
config.EncoderConfig.TimeKey = "timestamp"
|
||||||
|
config.EncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
|
||||||
|
logger, err := config.Build()
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf("Logger set up failed: %v", err))
|
||||||
|
}
|
||||||
|
defer logger.Sync()
|
||||||
|
return logger.Sugar()
|
||||||
|
}
|
||||||
+7
-7
@@ -1,8 +1,9 @@
|
|||||||
module main.go
|
module main.go
|
||||||
|
|
||||||
go 1.25.4
|
go 1.25.5
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/ceph/go-ceph v0.37.0
|
||||||
github.com/prometheus/client_golang v1.23.2
|
github.com/prometheus/client_golang v1.23.2
|
||||||
go.uber.org/zap v1.27.1
|
go.uber.org/zap v1.27.1
|
||||||
)
|
)
|
||||||
@@ -12,11 +13,10 @@ require (
|
|||||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||||
github.com/prometheus/client_model v0.6.2 // indirect
|
github.com/prometheus/client_model v0.6.2 // indirect
|
||||||
github.com/prometheus/common v0.67.4 // indirect
|
github.com/prometheus/common v0.66.1 // indirect
|
||||||
github.com/prometheus/procfs v0.19.2 // indirect
|
github.com/prometheus/procfs v0.16.1 // indirect
|
||||||
go.uber.org/multierr v1.11.0 // indirect
|
go.uber.org/multierr v1.10.0 // indirect
|
||||||
go.yaml.in/yaml/v2 v2.4.3 // indirect
|
go.yaml.in/yaml/v2 v2.4.2 // indirect
|
||||||
golang.org/x/sys v0.38.0 // indirect
|
golang.org/x/sys v0.38.0 // indirect
|
||||||
google.golang.org/protobuf v1.36.10 // indirect
|
google.golang.org/protobuf v1.36.8 // indirect
|
||||||
gopkg.in/pipe.v2 v2.0.0-20140414041502-3c2ca4d52544 // indirect
|
|
||||||
)
|
)
|
||||||
|
|||||||
+30
-14
@@ -1,37 +1,53 @@
|
|||||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||||
|
github.com/ceph/go-ceph v0.37.0 h1:KXliBe3ZDr3/AtfY7n9d1MG7ippYNCVhMPcAgm05CFI=
|
||||||
|
github.com/ceph/go-ceph v0.37.0/go.mod h1:3y2tOlITlyuVFhy8v6PpCEfjMwKPfXJiH0/2hKZZQRE=
|
||||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
|
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/gofrs/uuid/v5 v5.4.0 h1:EfbpCTjqMuGyq5ZJwxqzn3Cbr2d0rUZU7v5ycAk/e/0=
|
||||||
|
github.com/gofrs/uuid/v5 v5.4.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8=
|
||||||
|
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||||
|
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||||
|
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
|
||||||
|
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
|
||||||
|
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||||
|
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||||
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
|
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||||
|
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||||
|
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/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o=
|
github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o=
|
||||||
github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg=
|
github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg=
|
||||||
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
|
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
|
||||||
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
|
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
|
||||||
github.com/prometheus/common v0.66.1 h1:h5E0h5/Y8niHc5DlaLlWLArTQI7tMrsfQjHV+d9ZoGs=
|
github.com/prometheus/common v0.66.1 h1:h5E0h5/Y8niHc5DlaLlWLArTQI7tMrsfQjHV+d9ZoGs=
|
||||||
github.com/prometheus/common v0.66.1/go.mod h1:gcaUsgf3KfRSwHY4dIMXLPV0K/Wg1oZ8+SbZk/HH/dA=
|
github.com/prometheus/common v0.66.1/go.mod h1:gcaUsgf3KfRSwHY4dIMXLPV0K/Wg1oZ8+SbZk/HH/dA=
|
||||||
github.com/prometheus/common v0.67.4 h1:yR3NqWO1/UyO1w2PhUvXlGQs/PtFmoveVO0KZ4+Lvsc=
|
|
||||||
github.com/prometheus/common v0.67.4/go.mod h1:gP0fq6YjjNCLssJCQp0yk4M8W6ikLURwkdd/YKtTbyI=
|
|
||||||
github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg=
|
github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg=
|
||||||
github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is=
|
github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is=
|
||||||
github.com/prometheus/procfs v0.19.2 h1:zUMhqEW66Ex7OXIiDkll3tl9a1ZdilUOd/F6ZXw4Vws=
|
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
|
||||||
github.com/prometheus/procfs v0.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw=
|
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
|
||||||
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||||
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||||
|
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||||
|
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||||
|
go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ=
|
||||||
|
go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||||
go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc=
|
go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc=
|
||||||
go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
||||||
go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI=
|
go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI=
|
||||||
go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU=
|
go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU=
|
||||||
go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0=
|
|
||||||
go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8=
|
|
||||||
golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
|
|
||||||
golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
|
||||||
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
|
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
|
||||||
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||||
google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc=
|
google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc=
|
||||||
google.golang.org/protobuf v1.36.8/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
|
google.golang.org/protobuf v1.36.8/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
|
||||||
google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
|
|
||||||
google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/pipe.v2 v2.0.0-20140414041502-3c2ca4d52544 h1:WJH1qsOB4/zb/li+zLMn0vaAUJ5FqPv6HYLI3aQVg1k=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||||
gopkg.in/pipe.v2 v2.0.0-20140414041502-3c2ca4d52544/go.mod h1:UhTeH/yXCK/KY7TX24mqPkaQ7gZeqmWd/8SSS8B3aHw=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
|||||||
Executable
BIN
Binary file not shown.
+41
-212
@@ -1,236 +1,67 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
_ "os/exec"
|
"os"
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
prometheus "github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||||
promhttp "github.com/prometheus/client_golang/prometheus/promhttp"
|
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
"go.uber.org/zap/zapcore"
|
|
||||||
pipe "gopkg.in/pipe.v2"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var params Params
|
||||||
var logger *zap.SugaredLogger
|
var logger *zap.SugaredLogger
|
||||||
|
|
||||||
// Here I store all images info in scope of one pool
|
func getMetrics(cephConn CephConnector) {
|
||||||
type RBDUsage struct {
|
|
||||||
Images []RBDImageUsage
|
|
||||||
}
|
|
||||||
|
|
||||||
// Here I do collect info about images itself
|
|
||||||
type RBDImageUsage struct {
|
|
||||||
Name string `json:"name"`
|
|
||||||
id int `json: "id"`
|
|
||||||
RequestedSize uint64 `json:"provisioned_size"`
|
|
||||||
UsedSize uint64 `json: "used_size"`
|
|
||||||
}
|
|
||||||
|
|
||||||
var total_rbd_requested_size_per_pool = prometheus.NewGaugeVec(prometheus.GaugeOpts{
|
|
||||||
Name: "total_rbd_requested_size_per_pool",
|
|
||||||
Help: "total size of all requested RBDs in a specific pool",
|
|
||||||
},
|
|
||||||
[]string{"poolname"},
|
|
||||||
)
|
|
||||||
|
|
||||||
var total_rbd_requested_size = prometheus.NewGauge(prometheus.GaugeOpts{
|
|
||||||
Name: "total_rbd_requested_size",
|
|
||||||
Help: "total size of all RBDs in the cluster",
|
|
||||||
})
|
|
||||||
|
|
||||||
// Here I initialize logger and set some custom settings
|
|
||||||
func loggerInit() *zap.SugaredLogger {
|
|
||||||
config := zap.NewDevelopmentConfig()
|
|
||||||
config.EncoderConfig.TimeKey = "timestamp"
|
|
||||||
config.EncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
|
|
||||||
logger, err := config.Build()
|
|
||||||
if err != nil {
|
|
||||||
panic(fmt.Sprintf("Logger set up failed: %v", err))
|
|
||||||
}
|
|
||||||
defer logger.Sync()
|
|
||||||
return logger.Sugar()
|
|
||||||
}
|
|
||||||
|
|
||||||
// This func runs before main() to get all the things set up before main execution
|
|
||||||
func init() {
|
|
||||||
logger = loggerInit()
|
|
||||||
logger.Info("Setting up logger is complete successfully")
|
|
||||||
logger.Info("Registering prom metrics")
|
|
||||||
|
|
||||||
|
metrics := InitMetrics()
|
||||||
prometheus.MustRegister(
|
prometheus.MustRegister(
|
||||||
total_rbd_requested_size_per_pool,
|
metrics.Total_rbd_requested_size_per_pool,
|
||||||
total_rbd_requested_size,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// List pools with application rbd enabled
|
|
||||||
func listPools() ([]string, error) {
|
|
||||||
results := []string{}
|
|
||||||
command1 := "ceph"
|
|
||||||
args1 := []string{"osd", "pool", "ls", "detail"}
|
|
||||||
command2 := "grep"
|
|
||||||
args2 := []string{"application rbd"}
|
|
||||||
command3 := "awk"
|
|
||||||
args3 := []string{"{print $3}"}
|
|
||||||
|
|
||||||
logger.Infof("Listing pools")
|
|
||||||
|
|
||||||
// This is a pipe conjuction to execute "ceph | grep | awk" pipe
|
|
||||||
p := pipe.Line(
|
|
||||||
pipe.Exec(command1, args1...),
|
|
||||||
pipe.Exec(command2, args2...),
|
|
||||||
pipe.Exec(command3, args3...),
|
|
||||||
)
|
|
||||||
|
|
||||||
output, err := pipe.CombinedOutput(p)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
//Returns iterator
|
|
||||||
lines := strings.SplitSeq(string(output), "\n")
|
|
||||||
|
|
||||||
for v := range lines {
|
|
||||||
v = strings.TrimSpace(v)
|
|
||||||
// awk return result surrounded by single quotes so here I delete it
|
|
||||||
v = strings.Trim(v, "'")
|
|
||||||
// Sometimes here I have an empty string, that's why I check if it is not
|
|
||||||
if v != "" {
|
|
||||||
results = append(results, v)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return results, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// List rbd of each pool
|
|
||||||
func getRBD(poolList []string) map[string][]string {
|
|
||||||
RBDmap := make(map[string][]string)
|
|
||||||
args := []string{"ls", "-p"}
|
|
||||||
|
|
||||||
// Here I iterate over pool names
|
|
||||||
for _, v := range poolList {
|
|
||||||
var results []string
|
|
||||||
//...and start a command rbd ls -p <pool_name>
|
|
||||||
new_args := append(args, v)
|
|
||||||
p := pipe.Line(
|
|
||||||
pipe.Exec("rbd", new_args...),
|
|
||||||
)
|
|
||||||
output, err := pipe.CombinedOutput(p)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
//Returns iterator
|
|
||||||
lines := strings.SplitSeq(string(output), "\n")
|
|
||||||
|
|
||||||
for i := range lines {
|
|
||||||
//delete whitespaces around
|
|
||||||
i = strings.TrimSpace(i)
|
|
||||||
// check if there is no empty entries
|
|
||||||
if i != "" {
|
|
||||||
results = append(results, i)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
RBDmap[v] = results
|
|
||||||
}
|
|
||||||
return RBDmap
|
|
||||||
}
|
|
||||||
|
|
||||||
// Here I check total provisioned size of each RBD image in a pool
|
|
||||||
func RbdChecker(rbdMap map[string][]string) map[string][]RBDUsage{
|
|
||||||
total := make(map[string][]RBDUsage)
|
|
||||||
|
|
||||||
for pool, rbdlist := range rbdMap {
|
|
||||||
logger.Infof("Processing pool %s", pool)
|
|
||||||
|
|
||||||
for _, rbdName := range rbdlist {
|
|
||||||
total[pool] = append(total[pool],GetRBDStats(pool, rbdName))
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
logger.Debugf("Final map is %v",total)
|
|
||||||
return total
|
|
||||||
}
|
|
||||||
|
|
||||||
// Grabbing info about specific image
|
|
||||||
func GetRBDStats(pool string, rbdname string) RBDUsage {
|
|
||||||
rbdPath := fmt.Sprintf("%s/%s", pool, rbdname)
|
|
||||||
args := []string{"du", "--format", "json", rbdPath}
|
|
||||||
|
|
||||||
p := pipe.Line(
|
|
||||||
pipe.Exec("rbd", args...),
|
|
||||||
)
|
|
||||||
output, err := pipe.CombinedOutput(p)
|
|
||||||
if err != nil {
|
|
||||||
logger.Fatalf("Error in processing RBD %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var usage RBDUsage
|
|
||||||
if err := json.Unmarshal(output, &usage); err != nil {
|
|
||||||
logger.Fatalf("Error in unmarshaling %v", err)
|
|
||||||
}
|
|
||||||
return usage
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func FormMetrirs(rbdStats map[string][]RBDUsage) {
|
|
||||||
// this is requested size overall cluster
|
|
||||||
var totalSize uint64 = 0
|
|
||||||
|
|
||||||
// Iterate over pools
|
|
||||||
for poolName := range rbdStats {
|
|
||||||
var totalSizePerPool uint64 = 0
|
|
||||||
logger.Debugf("Forming metrics for pool %s",poolName)
|
|
||||||
|
|
||||||
//Iterate over all RBDs in the pool
|
|
||||||
for _,rbdName := range rbdStats[poolName] {
|
|
||||||
logger.Debugf("Processings rbd %v",rbdName)
|
|
||||||
totalSizePerPool = totalSizePerPool + rbdName.Images[0].RequestedSize
|
|
||||||
// logger.Debugf("RBD name is %s and its size is %d",rbdName.Images[0].Name,rbdName.Images[0].RequestedSize)
|
|
||||||
}
|
|
||||||
logger.Debugf("Total size requested by RBDs of a pool %s is %d bytes",poolName,totalSizePerPool)
|
|
||||||
|
|
||||||
total_rbd_requested_size_per_pool.WithLabelValues(
|
|
||||||
poolName,
|
|
||||||
).Set(float64(totalSizePerPool))
|
|
||||||
totalSize = totalSize + totalSizePerPool
|
|
||||||
}
|
|
||||||
logger.Debugf("Total size of all RBDs in a cluster is %d",totalSize)
|
|
||||||
total_rbd_requested_size.Set(float64(totalSize))
|
|
||||||
}
|
|
||||||
|
|
||||||
// the main loop for monitoting
|
|
||||||
func startCheking() {
|
|
||||||
|
|
||||||
//Ticks every 5 seconds
|
|
||||||
ticker := time.NewTicker(2 * time.Second)
|
ticker := time.NewTicker(2 * time.Second)
|
||||||
defer ticker.Stop()
|
defer ticker.Stop()
|
||||||
|
|
||||||
// Every tick I start listPools function
|
|
||||||
for range ticker.C {
|
for range ticker.C {
|
||||||
poolList, err := listPools()
|
var result []Pool = []Pool{}
|
||||||
|
poolList, err := cephConn.ListPools()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Fatalf("Error in listing pools %v", err)
|
logger.Error("Cannot get list of pools")
|
||||||
}
|
// do not exit but continue checking
|
||||||
// Get the map of all RBDs in all pools
|
continue
|
||||||
rbdMap := getRBD(poolList)
|
|
||||||
//Get all RBDs info
|
|
||||||
RBDStats := RbdChecker(rbdMap)
|
|
||||||
//Fill out metrics
|
|
||||||
FormMetrirs(RBDStats)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, v := range poolList {
|
||||||
|
x, _ := PoolFactory(cephConn, v)
|
||||||
|
result = append(result, x)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
metrics.Total_rbd_requested_size_per_pool.Reset()
|
||||||
|
for _,v := range result {
|
||||||
|
if !v.HasRBD {continue}
|
||||||
|
FillMetrics(v,metrics)
|
||||||
|
}
|
||||||
|
fmt.Println("====================")
|
||||||
|
// fmt.Println(result)
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
defer logger.Sync()
|
cephConn, err := connect()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
if wrapped := errors.Unwrap(err); wrapped != nil {
|
||||||
|
fmt.Println(wrapped)
|
||||||
|
}
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
defer cephConn.conn.Shutdown()
|
||||||
|
logger.Info("Successfully connected")
|
||||||
|
|
||||||
http.Handle("/metrics", promhttp.Handler())
|
http.Handle("/metrics", promhttp.Handler())
|
||||||
|
|
||||||
// HTTP runs in separate thread cuz it blocks futher execution of main
|
// HTTP runs in separate thread cuz it blocks futher execution of main
|
||||||
@@ -243,10 +74,8 @@ func main() {
|
|||||||
logger.Info("HTTP server started")
|
logger.Info("HTTP server started")
|
||||||
}()
|
}()
|
||||||
|
|
||||||
go func() {
|
go getMetrics(cephConn)
|
||||||
logger.Info("Start checking")
|
|
||||||
startCheking()
|
|
||||||
}()
|
|
||||||
|
|
||||||
select {}
|
select {}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
|
)
|
||||||
|
|
||||||
|
func InitMetrics() *Metrics {
|
||||||
|
m := &Metrics{
|
||||||
|
Total_rbd_requested_size_per_pool: prometheus.NewGaugeVec(
|
||||||
|
prometheus.GaugeOpts{
|
||||||
|
Name: "total_rbd_requested_size_per_pool",
|
||||||
|
Help: "total size of all requested RBDs in a specific pool",
|
||||||
|
},
|
||||||
|
[]string{"poolname"},
|
||||||
|
),
|
||||||
|
|
||||||
|
// total_rbd_requested_size: prometheus.NewGauge(
|
||||||
|
// prometheus.GaugeOpts{
|
||||||
|
// Name: "total_rbd_requested_size",
|
||||||
|
// Help: "total size of all RBDs in the cluster",
|
||||||
|
// },
|
||||||
|
// ),
|
||||||
|
}
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func FillMetrics(pool Pool,metrics *Metrics) {
|
||||||
|
var totalSizePerPool uint64 = 0
|
||||||
|
|
||||||
|
logger.Debugf("Processing pool %s",pool.Name)
|
||||||
|
for _,v := range pool.RBDlist {
|
||||||
|
totalSizePerPool += uint64(v.GetSize())
|
||||||
|
}
|
||||||
|
logger.Debugf("Total size of RBDs in pool %s is %d",pool.Name,totalSizePerPool)
|
||||||
|
|
||||||
|
metrics.Total_rbd_requested_size_per_pool.WithLabelValues(pool.Name,
|
||||||
|
).Set(
|
||||||
|
float64(totalSizePerPool))
|
||||||
|
|
||||||
|
}
|
||||||
Executable
BIN
Binary file not shown.
@@ -0,0 +1,115 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/ceph/go-ceph/rados"
|
||||||
|
"github.com/ceph/go-ceph/rbd"
|
||||||
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Params struct {
|
||||||
|
Config string
|
||||||
|
Keyring string
|
||||||
|
}
|
||||||
|
|
||||||
|
type CephConnector interface {
|
||||||
|
ListPools() ([]string,error)
|
||||||
|
OpenIOContext(poolName string) (IOContexter, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type CephConnection struct {
|
||||||
|
conn *rados.Conn
|
||||||
|
}
|
||||||
|
|
||||||
|
// Here I do implement CephConnector interface
|
||||||
|
func (cc CephConnection) ListPools() ([]string,error) {
|
||||||
|
return cc.conn.ListPools()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Here I do implement CephConnector interface
|
||||||
|
func (cc CephConnection) OpenIOContext(poolName string) (IOContexter,error) {
|
||||||
|
ioctx,err := cc.conn.OpenIOContext(poolName)
|
||||||
|
if err != nil {
|
||||||
|
return nil,err
|
||||||
|
}
|
||||||
|
return IOContextWrapper{ioctx: ioctx},nil
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
type IOContexter interface {
|
||||||
|
Destroy()
|
||||||
|
GetImageNames() ([]string, error)
|
||||||
|
GetImage(name string) RBDImager
|
||||||
|
}
|
||||||
|
|
||||||
|
type IOContextWrapper struct {
|
||||||
|
ioctx *rados.IOContext
|
||||||
|
}
|
||||||
|
|
||||||
|
func (iocw IOContextWrapper) Destroy() {
|
||||||
|
iocw.ioctx.Destroy()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (iocw IOContextWrapper) GetImageNames() ([]string,error){
|
||||||
|
return rbd.GetImageNames(iocw.ioctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (iocw IOContextWrapper) GetImage(rbdName string) RBDImager {
|
||||||
|
img := rbd.GetImage(iocw.ioctx,rbdName)
|
||||||
|
return RBDImageWrapper{image: img}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
type RBDImager interface {
|
||||||
|
Open() error
|
||||||
|
Close() error
|
||||||
|
Stat() (*rbd.ImageInfo,error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type RBDImageWrapper struct {
|
||||||
|
image *rbd.Image
|
||||||
|
}
|
||||||
|
|
||||||
|
func (riw RBDImageWrapper) Open() error {
|
||||||
|
return riw.image.Open()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (riw RBDImageWrapper) Close() error {
|
||||||
|
return riw.image.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (riw RBDImageWrapper) Stat() (*rbd.ImageInfo,error) {
|
||||||
|
return riw.image.Stat()
|
||||||
|
}
|
||||||
|
|
||||||
|
type iRBD interface {
|
||||||
|
GetName() string
|
||||||
|
GetSize() int64
|
||||||
|
}
|
||||||
|
|
||||||
|
type RBDUsage struct {
|
||||||
|
Images []RBD
|
||||||
|
}
|
||||||
|
|
||||||
|
type RBD struct {
|
||||||
|
Name string
|
||||||
|
rbd.ImageInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r RBD) GetName() string {
|
||||||
|
return r.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r RBD) GetSize() int64 {
|
||||||
|
return int64(r.Size)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Pool struct {
|
||||||
|
Name string
|
||||||
|
HasRBD bool
|
||||||
|
RBDlist []iRBD
|
||||||
|
}
|
||||||
|
|
||||||
|
type Metrics struct {
|
||||||
|
Total_rbd_requested_size_per_pool *prometheus.GaugeVec
|
||||||
|
Total_rbd_requested_size prometheus.Gauge
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
FROM golang:1.25-alpine AS builder
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
COPY go.mod go.sum ./
|
||||||
|
RUN go mod download
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
RUN CGO_ENABLED=0 GOOS=linux go build -o rbd-exporter .
|
||||||
|
|
||||||
|
FROM ubuntu:22.04
|
||||||
|
RUN apt-get update && apt-get install -y \
|
||||||
|
ceph-common \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
WORKDIR /home/
|
||||||
|
|
||||||
|
COPY --from=builder /app/rbd-exporter .
|
||||||
|
EXPOSE 9040
|
||||||
|
|
||||||
|
CMD [ "./rbd-exporter" ]
|
||||||
@@ -0,0 +1,151 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: export-deploy
|
||||||
|
namespace: rook-ceph
|
||||||
|
labels:
|
||||||
|
app: export-deploy
|
||||||
|
annotations:
|
||||||
|
prometheus.io/scrape: "true"
|
||||||
|
prometheus.io/port: "9040"
|
||||||
|
prometheus.io/path: "/metrics"
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: export-box
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: export-box
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: export
|
||||||
|
image: serviceplant/goexp:0.0.28
|
||||||
|
ports:
|
||||||
|
- containerPort: 9040
|
||||||
|
name: metrics
|
||||||
|
command:
|
||||||
|
- /bin/bash
|
||||||
|
- -c
|
||||||
|
- |
|
||||||
|
# Replicate the script from toolbox.sh inline so the ceph image
|
||||||
|
# can be run directly, instead of requiring the rook toolbox
|
||||||
|
CEPH_CONFIG="/etc/ceph/ceph.conf"
|
||||||
|
MON_CONFIG="/etc/rook/mon-endpoints"
|
||||||
|
KEYRING_FILE="/etc/ceph/keyring"
|
||||||
|
|
||||||
|
# create a ceph config file in its default location so ceph/rados tools can be used
|
||||||
|
# without specifying any arguments
|
||||||
|
write_endpoints() {
|
||||||
|
endpoints=$(cat ${MON_CONFIG})
|
||||||
|
|
||||||
|
# filter out the mon names
|
||||||
|
# external cluster can have numbers or hyphens in mon names, handling them in regex
|
||||||
|
# shellcheck disable=SC2001
|
||||||
|
mon_endpoints=$(echo "${endpoints}"| sed 's/[a-z0-9_-]\+=//g')
|
||||||
|
|
||||||
|
DATE=$(date)
|
||||||
|
echo "$DATE writing mon endpoints to ${CEPH_CONFIG}: ${endpoints}"
|
||||||
|
cat <<EOF > ${CEPH_CONFIG}
|
||||||
|
[global]
|
||||||
|
mon_host = ${mon_endpoints}
|
||||||
|
|
||||||
|
[client.admin]
|
||||||
|
keyring = ${KEYRING_FILE}
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
# watch the endpoints config file and update if the mon endpoints ever change
|
||||||
|
watch_endpoints() {
|
||||||
|
# get the timestamp for the target of the soft link
|
||||||
|
real_path=$(realpath ${MON_CONFIG})
|
||||||
|
initial_time=$(stat -c %Z "${real_path}")
|
||||||
|
while true; do
|
||||||
|
echo "I am watching!!!"
|
||||||
|
real_path=$(realpath ${MON_CONFIG})
|
||||||
|
latest_time=$(stat -c %Z "${real_path}")
|
||||||
|
|
||||||
|
if [[ "${latest_time}" != "${initial_time}" ]]; then
|
||||||
|
write_endpoints
|
||||||
|
initial_time=${latest_time}
|
||||||
|
fi
|
||||||
|
|
||||||
|
sleep 10
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# read the secret from an env var (for backward compatibility), or from the secret file
|
||||||
|
ceph_secret=${ROOK_CEPH_SECRET}
|
||||||
|
if [[ "$ceph_secret" == "" ]]; then
|
||||||
|
ceph_secret=$(cat /var/lib/rook-ceph-mon/secret.keyring)
|
||||||
|
fi
|
||||||
|
|
||||||
|
# create the keyring file
|
||||||
|
cat <<EOF > ${KEYRING_FILE}
|
||||||
|
[${ROOK_CEPH_USERNAME}]
|
||||||
|
key = ${ceph_secret}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# write the initial config file
|
||||||
|
write_endpoints
|
||||||
|
|
||||||
|
# continuously update the mon endpoints if they fail over
|
||||||
|
exec /home/exporter &
|
||||||
|
watch_endpoints
|
||||||
|
imagePullPolicy: IfNotPresent
|
||||||
|
tty: true
|
||||||
|
securityContext:
|
||||||
|
runAsNonRoot: true
|
||||||
|
runAsUser: 2016
|
||||||
|
runAsGroup: 2016
|
||||||
|
capabilities:
|
||||||
|
drop: ["ALL"]
|
||||||
|
env:
|
||||||
|
- name: ROOK_CEPH_USERNAME
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: rook-ceph-mon
|
||||||
|
key: ceph-username
|
||||||
|
volumeMounts:
|
||||||
|
- mountPath: /etc/ceph
|
||||||
|
name: ceph-config
|
||||||
|
- name: mon-endpoint-volume
|
||||||
|
mountPath: /etc/rook
|
||||||
|
- name: ceph-admin-secret
|
||||||
|
mountPath: /var/lib/rook-ceph-mon
|
||||||
|
readOnly: true
|
||||||
|
volumes:
|
||||||
|
- name: ceph-admin-secret
|
||||||
|
secret:
|
||||||
|
secretName: rook-ceph-mon
|
||||||
|
optional: false
|
||||||
|
items:
|
||||||
|
- key: ceph-secret
|
||||||
|
path: secret.keyring
|
||||||
|
- name: mon-endpoint-volume
|
||||||
|
configMap:
|
||||||
|
name: rook-ceph-mon-endpoints
|
||||||
|
items:
|
||||||
|
- key: data
|
||||||
|
path: mon-endpoints
|
||||||
|
- name: ceph-config
|
||||||
|
emptyDir: {}
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: export-service
|
||||||
|
namespace: rook-ceph
|
||||||
|
labels:
|
||||||
|
svc_app: export
|
||||||
|
rook_cluster: rook-ceph
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
app: export-box
|
||||||
|
ports:
|
||||||
|
- name: metrics
|
||||||
|
port: 9040
|
||||||
|
targetPort: 9040
|
||||||
|
protocol: TCP
|
||||||
|
type: ClusterIP
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
module main.go
|
||||||
|
|
||||||
|
go 1.25.4
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/prometheus/client_golang v1.23.2
|
||||||
|
go.uber.org/zap v1.27.1
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
|
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||||
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||||
|
github.com/prometheus/client_model v0.6.2 // indirect
|
||||||
|
github.com/prometheus/common v0.67.4 // indirect
|
||||||
|
github.com/prometheus/procfs v0.19.2 // indirect
|
||||||
|
go.uber.org/multierr v1.11.0 // indirect
|
||||||
|
go.yaml.in/yaml/v2 v2.4.3 // indirect
|
||||||
|
golang.org/x/sys v0.38.0 // indirect
|
||||||
|
google.golang.org/protobuf v1.36.10 // indirect
|
||||||
|
gopkg.in/pipe.v2 v2.0.0-20140414041502-3c2ca4d52544 // indirect
|
||||||
|
)
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||||
|
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||||
|
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||||
|
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||||
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||||
|
github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o=
|
||||||
|
github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg=
|
||||||
|
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
|
||||||
|
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
|
||||||
|
github.com/prometheus/common v0.66.1 h1:h5E0h5/Y8niHc5DlaLlWLArTQI7tMrsfQjHV+d9ZoGs=
|
||||||
|
github.com/prometheus/common v0.66.1/go.mod h1:gcaUsgf3KfRSwHY4dIMXLPV0K/Wg1oZ8+SbZk/HH/dA=
|
||||||
|
github.com/prometheus/common v0.67.4 h1:yR3NqWO1/UyO1w2PhUvXlGQs/PtFmoveVO0KZ4+Lvsc=
|
||||||
|
github.com/prometheus/common v0.67.4/go.mod h1:gP0fq6YjjNCLssJCQp0yk4M8W6ikLURwkdd/YKtTbyI=
|
||||||
|
github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg=
|
||||||
|
github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is=
|
||||||
|
github.com/prometheus/procfs v0.19.2 h1:zUMhqEW66Ex7OXIiDkll3tl9a1ZdilUOd/F6ZXw4Vws=
|
||||||
|
github.com/prometheus/procfs v0.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw=
|
||||||
|
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||||
|
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||||
|
go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc=
|
||||||
|
go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
||||||
|
go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI=
|
||||||
|
go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU=
|
||||||
|
go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0=
|
||||||
|
go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8=
|
||||||
|
golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
|
||||||
|
golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||||
|
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
|
||||||
|
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||||
|
google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc=
|
||||||
|
google.golang.org/protobuf v1.36.8/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
|
||||||
|
google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
|
||||||
|
google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/pipe.v2 v2.0.0-20140414041502-3c2ca4d52544 h1:WJH1qsOB4/zb/li+zLMn0vaAUJ5FqPv6HYLI3aQVg1k=
|
||||||
|
gopkg.in/pipe.v2 v2.0.0-20140414041502-3c2ca4d52544/go.mod h1:UhTeH/yXCK/KY7TX24mqPkaQ7gZeqmWd/8SSS8B3aHw=
|
||||||
@@ -0,0 +1,267 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
_ "os/exec"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
prometheus "github.com/prometheus/client_golang/prometheus"
|
||||||
|
promhttp "github.com/prometheus/client_golang/prometheus/promhttp"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
"go.uber.org/zap/zapcore"
|
||||||
|
pipe "gopkg.in/pipe.v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
var logger *zap.SugaredLogger
|
||||||
|
|
||||||
|
// Here I store all images info in scope of one pool
|
||||||
|
type RBDUsage struct {
|
||||||
|
Images []RBDImageUsage
|
||||||
|
}
|
||||||
|
|
||||||
|
// Here I do collect info about images itself
|
||||||
|
type RBDImageUsage struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
id int `json: "id"`
|
||||||
|
RequestedSize uint64 `json:"provisioned_size"`
|
||||||
|
UsedSize uint64 `json: "used_size"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var total_rbd_requested_size_per_pool = prometheus.NewGaugeVec(prometheus.GaugeOpts{
|
||||||
|
Name: "total_rbd_requested_size_per_pool",
|
||||||
|
Help: "total size of all requested RBDs in a specific pool",
|
||||||
|
},
|
||||||
|
[]string{"poolname"},
|
||||||
|
)
|
||||||
|
|
||||||
|
var total_rbd_requested_size = prometheus.NewGauge(prometheus.GaugeOpts{
|
||||||
|
Name: "total_rbd_requested_size",
|
||||||
|
Help: "total size of all RBDs in the cluster",
|
||||||
|
})
|
||||||
|
|
||||||
|
// Here I initialize logger and set some custom settings
|
||||||
|
func loggerInit() *zap.SugaredLogger {
|
||||||
|
config := zap.NewDevelopmentConfig()
|
||||||
|
config.EncoderConfig.TimeKey = "timestamp"
|
||||||
|
config.EncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
|
||||||
|
logger, err := config.Build()
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf("Logger set up failed: %v", err))
|
||||||
|
}
|
||||||
|
defer logger.Sync()
|
||||||
|
return logger.Sugar()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// List pools with application rbd enabled
|
||||||
|
func listPools() ([]string, error) {
|
||||||
|
results := []string{}
|
||||||
|
command1 := "ceph"
|
||||||
|
args1 := []string{"osd", "pool", "ls", "detail"}
|
||||||
|
command2 := "grep"
|
||||||
|
args2 := []string{"application rbd"}
|
||||||
|
command3 := "awk"
|
||||||
|
args3 := []string{"{print $3}"}
|
||||||
|
|
||||||
|
logger.Infof("Listing pools")
|
||||||
|
|
||||||
|
// This is a pipe conjuction to execute "ceph | grep | awk" pipe
|
||||||
|
p := pipe.Line(
|
||||||
|
pipe.Exec(command1, args1...),
|
||||||
|
pipe.Exec(command2, args2...),
|
||||||
|
pipe.Exec(command3, args3...),
|
||||||
|
)
|
||||||
|
|
||||||
|
output, err := pipe.CombinedOutput(p)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
//Returns iterator
|
||||||
|
lines := strings.SplitSeq(string(output), "\n")
|
||||||
|
|
||||||
|
for v := range lines {
|
||||||
|
v = strings.TrimSpace(v)
|
||||||
|
// awk return result surrounded by single quotes so here I delete it
|
||||||
|
v = strings.Trim(v, "'")
|
||||||
|
// Sometimes here I have an empty string, that's why I check if it is not
|
||||||
|
if v != "" {
|
||||||
|
results = append(results, v)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return results, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// List rbd of each pool
|
||||||
|
func getRBD(poolList []string) map[string][]string {
|
||||||
|
RBDmap := make(map[string][]string)
|
||||||
|
args := []string{"ls", "-p"}
|
||||||
|
|
||||||
|
// Here I iterate over pool names
|
||||||
|
for _, v := range poolList {
|
||||||
|
var results []string
|
||||||
|
//...and start a command rbd ls -p <pool_name>
|
||||||
|
new_args := append(args, v)
|
||||||
|
p := pipe.Line(
|
||||||
|
pipe.Exec("rbd", new_args...),
|
||||||
|
)
|
||||||
|
output, err := pipe.CombinedOutput(p)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
//Returns iterator
|
||||||
|
lines := strings.SplitSeq(string(output), "\n")
|
||||||
|
|
||||||
|
for i := range lines {
|
||||||
|
//delete whitespaces around
|
||||||
|
i = strings.TrimSpace(i)
|
||||||
|
// check if there is no empty entries
|
||||||
|
if i != "" {
|
||||||
|
results = append(results, i)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RBDmap[v] = results
|
||||||
|
}
|
||||||
|
return RBDmap
|
||||||
|
}
|
||||||
|
|
||||||
|
// Here I check total provisioned size of each RBD image in a pool
|
||||||
|
func RbdChecker(rbdMap map[string][]string) map[string][]RBDUsage{
|
||||||
|
total := make(map[string][]RBDUsage)
|
||||||
|
|
||||||
|
for pool, rbdlist := range rbdMap {
|
||||||
|
logger.Infof("Processing pool %s", pool)
|
||||||
|
|
||||||
|
for _, rbdName := range rbdlist {
|
||||||
|
total[pool] = append(total[pool],GetRBDStats(pool, rbdName))
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
logger.Debugf("Final map is %v",total)
|
||||||
|
return total
|
||||||
|
}
|
||||||
|
|
||||||
|
// Grabbing info about specific image
|
||||||
|
func GetRBDStats(pool string, rbdname string) RBDUsage {
|
||||||
|
rbdPath := fmt.Sprintf("%s/%s", pool, rbdname)
|
||||||
|
args := []string{"du", "--format", "json", rbdPath}
|
||||||
|
|
||||||
|
p := pipe.Line(
|
||||||
|
pipe.Exec("rbd", args...),
|
||||||
|
)
|
||||||
|
output, err := pipe.CombinedOutput(p)
|
||||||
|
if err != nil {
|
||||||
|
logger.Fatalf("Error in processing RBD %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var usage RBDUsage
|
||||||
|
if err := json.Unmarshal(output, &usage); err != nil {
|
||||||
|
logger.Fatalf("Error in unmarshaling %v", err)
|
||||||
|
}
|
||||||
|
return usage
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func FormMetrirs(rbdStats map[string][]RBDUsage) {
|
||||||
|
// this is requested size overall cluster
|
||||||
|
var totalSize uint64 = 0
|
||||||
|
|
||||||
|
// Iterate over pools
|
||||||
|
for poolName := range rbdStats {
|
||||||
|
var totalSizePerPool uint64 = 0
|
||||||
|
logger.Debugf("Forming metrics for pool %s",poolName)
|
||||||
|
|
||||||
|
//Iterate over all RBDs in the pool
|
||||||
|
for _,rbdName := range rbdStats[poolName] {
|
||||||
|
logger.Debugf("Processings rbd %v",rbdName)
|
||||||
|
totalSizePerPool = totalSizePerPool + rbdName.Images[0].RequestedSize
|
||||||
|
// logger.Debugf("RBD name is %s and its size is %d",rbdName.Images[0].Name,rbdName.Images[0].RequestedSize)
|
||||||
|
}
|
||||||
|
logger.Debugf("Total size requested by RBDs of a pool %s is %d bytes",poolName,totalSizePerPool)
|
||||||
|
|
||||||
|
total_rbd_requested_size_per_pool.WithLabelValues(
|
||||||
|
poolName,
|
||||||
|
).Set(float64(totalSizePerPool))
|
||||||
|
totalSize = totalSize + totalSizePerPool
|
||||||
|
}
|
||||||
|
logger.Debugf("Total size of all RBDs in a cluster is %d",totalSize)
|
||||||
|
total_rbd_requested_size.Set(float64(totalSize))
|
||||||
|
}
|
||||||
|
|
||||||
|
// the main loop for monitoting
|
||||||
|
func startCheking() {
|
||||||
|
|
||||||
|
//Ticks every 5 seconds
|
||||||
|
ticker := time.NewTicker(2 * time.Second)
|
||||||
|
defer ticker.Stop()
|
||||||
|
|
||||||
|
// Every tick I start listPools function
|
||||||
|
for range ticker.C {
|
||||||
|
poolList, err := listPools()
|
||||||
|
if err != nil {
|
||||||
|
logger.Fatalf("Error in listing pools %v", err)
|
||||||
|
}
|
||||||
|
// Get the map of all RBDs in all pools
|
||||||
|
rbdMap := getRBD(poolList)
|
||||||
|
//Get all RBDs info
|
||||||
|
RBDStats := RbdChecker(rbdMap)
|
||||||
|
//Fill out metrics
|
||||||
|
FormMetrirs(RBDStats)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// This func runs before main() to get all the things set up before main execution
|
||||||
|
func init() {
|
||||||
|
logger = loggerInit()
|
||||||
|
logger.Info("Setting up logger is complete successfully")
|
||||||
|
logger.Info("Registering prom metrics")
|
||||||
|
|
||||||
|
prometheus.MustRegister(
|
||||||
|
total_rbd_requested_size_per_pool,
|
||||||
|
total_rbd_requested_size,
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
type IPoolLister interface {
|
||||||
|
listPools() ([]string,error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type IRbdFinder interface {
|
||||||
|
getRBD(poollist []string) map[string][]string
|
||||||
|
}
|
||||||
|
|
||||||
|
type IRbdChecker interface {
|
||||||
|
rbdChecker(rbdMap map[string][]string) map[string][]RBDUsage
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
defer logger.Sync()
|
||||||
|
http.Handle("/metrics", promhttp.Handler())
|
||||||
|
|
||||||
|
// HTTP runs in separate thread cuz it blocks futher execution of main
|
||||||
|
go func() {
|
||||||
|
logger.Info("Starting http server")
|
||||||
|
// Here I check for errors if HTTP fails
|
||||||
|
if err := http.ListenAndServe(":9040", nil); err != nil {
|
||||||
|
logger.Fatalf("HTTP server failed to start %v", err)
|
||||||
|
}
|
||||||
|
logger.Info("HTTP server started")
|
||||||
|
}()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
logger.Info("Start checking")
|
||||||
|
startCheking()
|
||||||
|
}()
|
||||||
|
|
||||||
|
select {}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
apiVersion: monitoring.coreos.com/v1
|
||||||
|
kind: ServiceMonitor
|
||||||
|
metadata:
|
||||||
|
name: export-monitor
|
||||||
|
namespace: rook-ceph
|
||||||
|
labels:
|
||||||
|
team: rook
|
||||||
|
spec:
|
||||||
|
namespaceSelector:
|
||||||
|
matchNames:
|
||||||
|
- rook-ceph
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
svc_app: export
|
||||||
|
rook_cluster: rook-ceph
|
||||||
|
endpoints:
|
||||||
|
- port: metrics
|
||||||
|
interval: 30s
|
||||||
|
path: /metrics
|
||||||
|
honorLabels: true
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
module main
|
||||||
|
|
||||||
|
go 1.25.6
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Res struct {
|
||||||
|
num int
|
||||||
|
name string
|
||||||
|
}
|
||||||
|
|
||||||
|
func Route(i int,k string, ch chan<- Res) {
|
||||||
|
ch <- Res{
|
||||||
|
num: i,
|
||||||
|
name: k,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
ch := make(chan Res,20)
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
|
||||||
|
for i:= range 10 {
|
||||||
|
for _,k := range []string{"thr1","thr2"} {
|
||||||
|
wg.Add(1)
|
||||||
|
go func () {
|
||||||
|
defer wg.Done()
|
||||||
|
Route(i, k, ch)
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
wg.Wait()
|
||||||
|
close(ch)
|
||||||
|
|
||||||
|
for i := range ch {
|
||||||
|
fmt.Println(i)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
module mod_prj
|
||||||
|
|
||||||
|
go 1.25.5
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
|
||||||
|
import "mod_prj/message"
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
|
||||||
|
messages.Hello_en()
|
||||||
|
messages.Bye_en()
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package messages
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
func Hello_en() {
|
||||||
|
|
||||||
|
fmt.Println("Hello")
|
||||||
|
}
|
||||||
|
func Bye_en() {
|
||||||
|
|
||||||
|
fmt.Println("Good Bye")
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
type IGun interface {
|
||||||
|
setName(name string)
|
||||||
|
setPower(power int)
|
||||||
|
getName() string
|
||||||
|
getPower() int
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
type AK47 struct {
|
||||||
|
Gun
|
||||||
|
}
|
||||||
|
|
||||||
|
func newAk47() IGun {
|
||||||
|
return AK47{
|
||||||
|
Gun: Gun{
|
||||||
|
name: "AK47",
|
||||||
|
power: 4,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
module main.go
|
||||||
|
|
||||||
|
go 1.25.5
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
type Gun struct {
|
||||||
|
name string
|
||||||
|
power int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g Gun) setName(name string) {
|
||||||
|
g.name = name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g Gun) setPower(power int) {
|
||||||
|
g.power = power
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g Gun) getName() string {
|
||||||
|
return g.name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g Gun) getPower() int {
|
||||||
|
return g.power
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import "errors"
|
||||||
|
|
||||||
|
func GetGun(gunType string) (IGun,error){
|
||||||
|
if gunType == "ak47" {
|
||||||
|
return newAk47(),nil
|
||||||
|
}
|
||||||
|
if gunType == "musket" {
|
||||||
|
return newMusket(),nil
|
||||||
|
}
|
||||||
|
return nil,errors.New("No such a gun")
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
func PrintDetails(gunName IGun) {
|
||||||
|
fmt.Println("Gun model is ",gunName.getName())
|
||||||
|
fmt.Println("Gun power is ",gunName.getPower())
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
ak47,err := GetGun("ak47")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
musket,err := GetGun("musket")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
PrintDetails(ak47)
|
||||||
|
PrintDetails(musket)
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
type musket struct {
|
||||||
|
Gun
|
||||||
|
}
|
||||||
|
|
||||||
|
func newMusket() IGun {
|
||||||
|
return musket{
|
||||||
|
Gun: Gun{
|
||||||
|
name: "Musket gun",
|
||||||
|
power: 1,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
# IntelliJ IDEA
|
||||||
|
.idea
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
*.iws
|
||||||
|
|
||||||
|
# macOS
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
# Golang
|
||||||
|
*.prof
|
||||||
|
*.exe
|
||||||
|
*.test
|
||||||
|
*.report
|
||||||
|
*.out
|
||||||
|
/bin/
|
||||||
|
/tmp/
|
||||||
|
/.build/
|
||||||
|
/vendor/
|
||||||
|
|
||||||
|
# test results
|
||||||
|
coverage.txt
|
||||||
|
coverage.xml
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
# IntelliJ IDEA
|
||||||
|
.idea/
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
*.iws
|
||||||
|
|
||||||
|
dist/
|
||||||
|
|
||||||
|
# VSCode
|
||||||
|
.vscode/
|
||||||
|
|
||||||
|
# zed
|
||||||
|
.zed/
|
||||||
|
|
||||||
|
# macOS
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
# Golang
|
||||||
|
*.prof
|
||||||
|
*.exe
|
||||||
|
*.test
|
||||||
|
*.report
|
||||||
|
*.out
|
||||||
|
/bin/
|
||||||
|
/tmp/
|
||||||
|
/.build/
|
||||||
|
/.devp/
|
||||||
|
/vendor/
|
||||||
|
go.work
|
||||||
|
go.work.sum
|
||||||
|
|
||||||
|
# test results
|
||||||
|
coverage.txt
|
||||||
|
coverage.xml
|
||||||
|
|
||||||
|
# Platform
|
||||||
|
scripts/golangci-merged.yml
|
||||||
|
graph.svg
|
||||||
|
devp_lint_report.json
|
||||||
|
.public
|
||||||
|
/dist/
|
||||||
|
|
||||||
|
# Other
|
||||||
|
*.tmp
|
||||||
|
.fuse_*
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
# Multi-stage build using official Go image
|
||||||
|
FROM harbor.mws-team.ru/docker.io/library/ubuntu:22.04 AS builder
|
||||||
|
|
||||||
|
# Add Ceph Reef repository
|
||||||
|
RUN apt-get update && apt-get install -y \
|
||||||
|
wget \
|
||||||
|
gnupg \
|
||||||
|
gcc \
|
||||||
|
pkg-config \
|
||||||
|
&& wget -q -O- 'https://download.ceph.com/keys/release.asc' | apt-key add - \
|
||||||
|
&& echo "deb https://download.ceph.com/debian-reef/ jammy main" > /etc/apt/sources.list.d/ceph.list
|
||||||
|
|
||||||
|
# Install Ceph Reef development libraries
|
||||||
|
RUN apt-get update && apt-get install -y \
|
||||||
|
librbd-dev \
|
||||||
|
librados-dev \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# Install Go
|
||||||
|
ENV GO_VERSION=1.25.5
|
||||||
|
RUN wget https://go.dev/dl/go${GO_VERSION}.linux-amd64.tar.gz \
|
||||||
|
&& tar -C /usr/local -xzf go${GO_VERSION}.linux-amd64.tar.gz \
|
||||||
|
&& rm go${GO_VERSION}.linux-amd64.tar.gz
|
||||||
|
|
||||||
|
# Set Go environment variables
|
||||||
|
ENV PATH=$PATH:/usr/local/go/bin
|
||||||
|
ENV GOPATH=/go
|
||||||
|
ENV PATH=$PATH:$GOPATH/bin
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Copy go mod files
|
||||||
|
COPY go.mod go.sum ./
|
||||||
|
RUN go mod download
|
||||||
|
|
||||||
|
# Copy source code
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# Build the application
|
||||||
|
RUN CGO_ENABLED=1 GOOS=linux go build -o rbd-exporter .
|
||||||
|
|
||||||
|
|
||||||
|
# Final stage with Ceph Reef runtime libraries
|
||||||
|
FROM harbor.mws-team.ru/docker.io/library/ubuntu:22.04 AS final
|
||||||
|
|
||||||
|
# Add Ceph Reef repository to runtime stage too
|
||||||
|
RUN apt-get update && apt-get install -y \
|
||||||
|
wget \
|
||||||
|
gnupg \
|
||||||
|
lsb-release \
|
||||||
|
ca-certificates \
|
||||||
|
&& wget -q -O- 'https://download.ceph.com/keys/release.asc' | gpg --dearmor -o /etc/apt/trusted.gpg.d/ceph.gpg \
|
||||||
|
&& echo "deb https://download.ceph.com/debian-reef/ $(lsb_release -cs) main" > /etc/apt/sources.list.d/ceph.list
|
||||||
|
|
||||||
|
# Install Ceph Reef runtime libraries
|
||||||
|
RUN apt-get update && apt-get install -y \
|
||||||
|
librbd-dev \
|
||||||
|
librados-dev \
|
||||||
|
ceph-common \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
WORKDIR /rbd-exporter
|
||||||
|
|
||||||
|
# Copy the binary from builder stage
|
||||||
|
COPY --from=builder /app/rbd-exporter .
|
||||||
|
|
||||||
|
# Make binary executable
|
||||||
|
RUN chmod +x ./rbd-exporter
|
||||||
|
|
||||||
|
EXPOSE 9040
|
||||||
|
|
||||||
|
CMD ["./rbd-exporter"]
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
include scripts/devp/platform.mk
|
||||||
|
|
||||||
|
.PHONY: generate
|
||||||
|
generate: devp/generate-go
|
||||||
|
|
||||||
|
.PHONY: test
|
||||||
|
test: devp/test-go
|
||||||
|
|
||||||
|
.PHONY: lint
|
||||||
|
lint: devp/lint-go-default
|
||||||
|
|
||||||
|
.PHONY: format
|
||||||
|
format: devp/format-go
|
||||||
|
|
||||||
|
.PHONY: go-mod-tidy
|
||||||
|
go-mod-tidy: devp/go-mod-tidy
|
||||||
|
|
||||||
|
.PHONY: ci/check
|
||||||
|
ci/check: devp/ci/check
|
||||||
|
@# This target is called during Big Bang 'Validate' CI job
|
||||||
|
|
||||||
|
.PHONY: ci/test/unit
|
||||||
|
ci/test/unit: devp/coverage
|
||||||
|
@# This target is called during Big Bang 'Test' CI job
|
||||||
|
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
package connection
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"rbd_exporter/mytypes"
|
||||||
|
"github.com/ceph/go-ceph/rados"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Connect(params mytypes.Params) (_ mytypes.CephConnection, err error) {
|
||||||
|
var cephConn = mytypes.CephConnection{}
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("error in func connect() %w", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
cephConn.Conn, err = rados.NewConnWithClusterAndUser("ceph", "client.admin")
|
||||||
|
if err != nil {
|
||||||
|
return mytypes.CephConnection{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = cephConn.Conn.ReadConfigFile(params.Config)
|
||||||
|
if err != nil {
|
||||||
|
return mytypes.CephConnection{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = cephConn.Conn.SetConfigOption("keyring", params.Keyring)
|
||||||
|
if err != nil {
|
||||||
|
return mytypes.CephConnection{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = cephConn.Conn.Connect()
|
||||||
|
if err != nil {
|
||||||
|
return mytypes.CephConnection{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return cephConn, nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,158 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: rbd-exporter
|
||||||
|
namespace: dev-unstable-rook-ceph-common
|
||||||
|
labels:
|
||||||
|
app: rbd-exporter
|
||||||
|
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: rbd-exporter
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: rbd-exporter
|
||||||
|
annotations:
|
||||||
|
obs.mws.ru/path: /metrics
|
||||||
|
obs.mws.ru/port: "9040"
|
||||||
|
obs.mws.ru/scheme: "http"
|
||||||
|
obs.mws.ru/scrape: "true"
|
||||||
|
obs.mws.ru/probe-scrape-interval: "1m"
|
||||||
|
spec:
|
||||||
|
imagePullSecrets:
|
||||||
|
- name: ceph-registry-secret
|
||||||
|
containers:
|
||||||
|
- name: rbd-exporter
|
||||||
|
image: harbor.mws-team.ru/mws-storage-junk/rbd-exporter:0.01
|
||||||
|
ports:
|
||||||
|
- containerPort: 9040
|
||||||
|
name: metrics
|
||||||
|
command:
|
||||||
|
- /bin/bash
|
||||||
|
- -c
|
||||||
|
- |
|
||||||
|
# Replicate the script from toolbox.sh inline so the ceph image
|
||||||
|
# can be run directly, instead of requiring the rook toolbox
|
||||||
|
CEPH_CONFIG="/etc/ceph/ceph.conf"
|
||||||
|
MON_CONFIG="/etc/rook/mon-endpoints"
|
||||||
|
KEYRING_FILE="/etc/ceph/keyring"
|
||||||
|
|
||||||
|
# create a ceph config file in its default location so ceph/rados tools can be used
|
||||||
|
# without specifying any arguments
|
||||||
|
write_endpoints() {
|
||||||
|
endpoints=$(cat ${MON_CONFIG})
|
||||||
|
|
||||||
|
# filter out the mon names
|
||||||
|
# external cluster can have numbers or hyphens in mon names, handling them in regex
|
||||||
|
# shellcheck disable=SC2001
|
||||||
|
mon_endpoints=$(echo "${endpoints}"| sed 's/[a-z0-9_-]\+=//g')
|
||||||
|
|
||||||
|
DATE=$(date)
|
||||||
|
echo "$DATE writing mon endpoints to ${CEPH_CONFIG}: ${endpoints}"
|
||||||
|
cat <<EOF > ${CEPH_CONFIG}
|
||||||
|
[global]
|
||||||
|
mon_host = ${mon_endpoints}
|
||||||
|
|
||||||
|
[client.admin]
|
||||||
|
keyring = ${KEYRING_FILE}
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
# watch the endpoints config file and update if the mon endpoints ever change
|
||||||
|
watch_endpoints() {
|
||||||
|
# get the timestamp for the target of the soft link
|
||||||
|
real_path=$(realpath ${MON_CONFIG})
|
||||||
|
initial_time=$(stat -c %Z "${real_path}")
|
||||||
|
while true; do
|
||||||
|
echo "I am watching for mon changes!!!"
|
||||||
|
real_path=$(realpath ${MON_CONFIG})
|
||||||
|
latest_time=$(stat -c %Z "${real_path}")
|
||||||
|
|
||||||
|
if [[ "${latest_time}" != "${initial_time}" ]]; then
|
||||||
|
write_endpoints
|
||||||
|
initial_time=${latest_time}
|
||||||
|
fi
|
||||||
|
|
||||||
|
sleep 10
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# read the secret from an env var (for backward compatibility), or from the secret file
|
||||||
|
ceph_secret=${ROOK_CEPH_SECRET}
|
||||||
|
if [[ "$ceph_secret" == "" ]]; then
|
||||||
|
ceph_secret=$(cat /var/lib/rook-ceph-mon/secret.keyring)
|
||||||
|
fi
|
||||||
|
|
||||||
|
# create the keyring file
|
||||||
|
cat <<EOF > ${KEYRING_FILE}
|
||||||
|
[${ROOK_CEPH_USERNAME}]
|
||||||
|
key = ${ceph_secret}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# write the initial config file
|
||||||
|
write_endpoints
|
||||||
|
|
||||||
|
# run rbd-exporter
|
||||||
|
exec /rbd-exporter/rbd-exporter --keyring /etc/ceph/keyring &
|
||||||
|
|
||||||
|
# continuously update the mon endpoints if they fail over
|
||||||
|
watch_endpoints
|
||||||
|
imagePullPolicy: IfNotPresent
|
||||||
|
tty: true
|
||||||
|
securityContext:
|
||||||
|
runAsNonRoot: true
|
||||||
|
runAsUser: 2016
|
||||||
|
runAsGroup: 2016
|
||||||
|
capabilities:
|
||||||
|
drop: ["ALL"]
|
||||||
|
env:
|
||||||
|
- name: ROOK_CEPH_USERNAME
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: rook-ceph-mon
|
||||||
|
key: ceph-username
|
||||||
|
volumeMounts:
|
||||||
|
- mountPath: /etc/ceph
|
||||||
|
name: ceph-config
|
||||||
|
- name: mon-endpoint-volume
|
||||||
|
mountPath: /etc/rook
|
||||||
|
- name: ceph-admin-secret
|
||||||
|
mountPath: /var/lib/rook-ceph-mon
|
||||||
|
readOnly: true
|
||||||
|
volumes:
|
||||||
|
- name: ceph-admin-secret
|
||||||
|
secret:
|
||||||
|
secretName: rook-ceph-mon
|
||||||
|
optional: false
|
||||||
|
items:
|
||||||
|
- key: ceph-secret
|
||||||
|
path: secret.keyring
|
||||||
|
- name: mon-endpoint-volume
|
||||||
|
configMap:
|
||||||
|
name: rook-ceph-mon-endpoints
|
||||||
|
items:
|
||||||
|
- key: data
|
||||||
|
path: mon-endpoints
|
||||||
|
- name: ceph-config
|
||||||
|
emptyDir: {}
|
||||||
|
# ---
|
||||||
|
# apiVersion: v1
|
||||||
|
# kind: Service
|
||||||
|
# metadata:
|
||||||
|
# name: export-service
|
||||||
|
# namespace: rook-ceph
|
||||||
|
# labels:
|
||||||
|
# svc_app: export
|
||||||
|
# rook_cluster: rook-ceph
|
||||||
|
# spec:
|
||||||
|
# selector:
|
||||||
|
# app: export-box
|
||||||
|
# ports:
|
||||||
|
# - name: metrics
|
||||||
|
# port: 9040
|
||||||
|
# targetPort: 9040
|
||||||
|
# protocol: TCP
|
||||||
|
# type: ClusterIP
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
# DEVP это файл для описания того, что именно будет деплоится
|
||||||
|
version: 3
|
||||||
|
cloud:
|
||||||
|
service: "storage"
|
||||||
|
# Имя программы
|
||||||
|
rbd-exporter:
|
||||||
|
type: "cloud" #дефолтное значение, если обычный проект из гита
|
||||||
|
# Список стендов находится здесь https://git.mws-team.ru/mws/stands
|
||||||
|
# Список: dev, preprod, prod, stand24Q1(env STAGE), stress-common(env STRESS)
|
||||||
|
stands:
|
||||||
|
- "dev"
|
||||||
|
build:
|
||||||
|
template: "golang"
|
||||||
|
config:
|
||||||
|
# Версия билдера заполняется автоматически при обновлении (см. строчку ниже).
|
||||||
|
# Если нужно обновить руками - make devp/update
|
||||||
|
builder-version: 0.75.0
|
||||||
|
# По схемам смотреть тут https://confluence.mts.ru/pages/viewpage.action?pageId=1071927793#id-%D0%92%D0%B5%D1%80%D1%81%D0%B8%D0%BE%D0%BD%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D1%80%D0%B5%D0%BB%D0%B8%D0%B7%D0%BE%D0%B2-%D0%A1%D1%85%D0%B5%D0%BC%D0%B0app
|
||||||
|
version-schema: app
|
||||||
|
# Использовать харбор на harbor.mws-team.ru
|
||||||
|
use-new-harbor: true
|
||||||
|
pipeline:
|
||||||
|
# Путь в репе до главного файла проги
|
||||||
|
app-path: main.go
|
||||||
|
# Manual - запуск пайплайна вручную
|
||||||
|
mode: manual
|
||||||
|
docker:
|
||||||
|
# Про выбор канико читать тут https://mws.pages.mws-team.ru/devp/big-bang/generator/pages/reference/devp-yaml.html#infrastrukturnye-komponenty
|
||||||
|
kaniko:
|
||||||
|
# Выбирал самую свежую по отсутствию уязвимостей
|
||||||
|
builder-version: 2.3.1-1.23.2.1
|
||||||
|
context: .
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user