diff --git a/apps/main.go b/apps/main.go index b0d08dc..1ee7c6b 100644 --- a/apps/main.go +++ b/apps/main.go @@ -111,9 +111,10 @@ func (a *AppsProcessor) Update(name string, SSHPort int, HTTPPort int, image str } // UpdateResources updates various metrics saved in the database -func (a *AppsProcessor) UpdateResources(name string, state string, CPUUsage float64, memory int, diskUsageBytes int, diskUsageInodes int, flags detector.Flags) error { +func (a *AppsProcessor) UpdateResources(name string, state string, OOMKilled bool, CPUUsage float64, memory int, diskUsageBytes int, diskUsageInodes int, flags detector.Flags) error { err := a.DB.Model(&App{}).Where("name = ?", name).Updates(App{ State: state, + OOMKilled: OOMKilled, CPUUsage: CPUUsage, MemoryUsage: memory, DiskUsageBytes: diskUsageBytes, @@ -124,9 +125,10 @@ func (a *AppsProcessor) UpdateResources(name string, state string, CPUUsage floa } // UpdateState sets container's state -func (a *AppsProcessor) UpdateState(name string, state string) error { +func (a *AppsProcessor) UpdateState(name string, state string, OOMKilled bool) error { err := a.DB.Model(&App{}).Where("name = ?", name).Updates(App{ - State: state, + State: state, + OOMKilled: OOMKilled, }).Error return err } diff --git a/apps/types.go b/apps/types.go index f5d7191..e04fe18 100644 --- a/apps/types.go +++ b/apps/types.go @@ -31,6 +31,7 @@ type Label struct { // AppState contains info about runnint application, it's not saved in the database type AppState struct { State string `json:"state"` + OOMKilled bool `json:"oom_killed"` CPUUsage float64 `json:"cpu_usage"` // in percents MemoryUsage int `json:"memory_usage"` // in MB DiskUsageBytes int `json:"disk_usage_bytes"` @@ -78,6 +79,8 @@ type App struct { // Current status of the application (underlaying container) State string `json:"state"` + // True if the current container has been killed by OOM killer + OOMKilled bool `json:"oom_killed"` // CPU usage in percents CPUUsage float64 `json:"cpu_usage"` // in percents // Memory usage in bytes diff --git a/containers/docker.go b/containers/docker.go index bc2f814..eb1f03f 100644 --- a/containers/docker.go +++ b/containers/docker.go @@ -79,8 +79,10 @@ func (d *Driver) nameToID(name string) (string, error) { } // Status return current status of container with given name -func (d *Driver) Status(name string) (string, error) { - status := "unknown" +func (d *Driver) Status(name string) (ContainerStatus, error) { + status := ContainerStatus{ + Status: "unknown", + } cli, err := d.getClient() if err != nil { @@ -90,7 +92,8 @@ func (d *Driver) Status(name string) (string, error) { containerID, err := d.nameToID(name) if err != nil && err.Error() == "no container found" { - return "no-container", err + status.Status = "no-container" + return status, err } if err != nil { return status, err @@ -101,7 +104,10 @@ func (d *Driver) Status(name string) (string, error) { return status, err } - return info.State.Status, nil + status.Status = info.State.Status + status.OOMKilled = info.State.OOMKilled + + return status, nil } diff --git a/containers/stats.go b/containers/stats.go index ccb97be..41a3533 100644 --- a/containers/stats.go +++ b/containers/stats.go @@ -2,18 +2,18 @@ package containers // ContainerStats contains fields returned in docker stats function stream type ContainerStats struct { - Pids struct { - Current int `json:"current"` - } `json:"pids_stats"` - CPU struct { - Usage struct { - Total int64 `json:"total_usage"` - } `json:"cpu_usage"` - } `json:"cpu_stats"` - Memory struct { - Usage int `json:"usage"` - MaxUsage int `json:"max_usage"` - Limit int `json:"limit"` - } `json:"memory_stats"` - ID string `json:"id"` + Pids struct { + Current int `json:"current"` + } `json:"pids_stats"` + CPU struct { + Usage struct { + Total int64 `json:"total_usage"` + } `json:"cpu_usage"` + } `json:"cpu_stats"` + Memory struct { + Usage int `json:"usage"` + MaxUsage int `json:"max_usage"` + Limit int `json:"limit"` + } `json:"memory_stats"` + ID string `json:"id"` } diff --git a/containers/types.go b/containers/types.go index 158a700..2b83854 100644 --- a/containers/types.go +++ b/containers/types.go @@ -21,6 +21,12 @@ type Process struct { State string `json:"state"` } +// Current status of the container +type ContainerStatus struct { + Status string `json:"status"` + OOMKilled bool `json:"oom_killed"` +} + // Container extends App struct from App type Container struct { App *apps.App `json:"app"` @@ -80,7 +86,8 @@ func (c *Container) GetState() (*apps.AppState, error) { } state := apps.AppState{ - State: status, + State: status.Status, + OOMKilled: status.OOMKilled, // CPUUsage: cpu, // MemoryUsage: memory, CPUUsage: -1.0, @@ -95,21 +102,21 @@ func (c *Container) GetState() (*apps.AppState, error) { // Status returns state of the container // Possible values: running, exited (stopped), no-container, unknown -func (c *Container) Status() (string, error) { - status := "unknown" +func (c *Container) Status() (ContainerStatus, error) { + status := ContainerStatus{ + Status: "unknown", + } driver := c.getDriver() containerStatus, err := driver.Status(c.App.Name) if err != nil && err.Error() == "no container found" { - return "no-container", nil + return ContainerStatus{Status: "no-container"}, nil } if err != nil { return status, err } - status = containerStatus - - return status, nil + return containerStatus, nil } // DiskUsage returns number of bytes and inodes used by the container in it's mounted volume @@ -190,7 +197,7 @@ func (c *Container) Delete() error { // does two things, deleting the container and the data and when // the deleted container doesn't exist we actually don't care // and we can continue to remove the data. - if status != "no-container" { + if status.Status != "no-container" { err = c.Destroy() if err != nil { return err diff --git a/glue/main.go b/glue/main.go index d51f0f2..53ccfdb 100644 --- a/glue/main.go +++ b/glue/main.go @@ -94,7 +94,7 @@ func (p *Processor) waitForApp() error { if err != nil { return err } - if status == "running" { + if status.Status == "running" { return nil } @@ -173,7 +173,7 @@ func (p *Processor) Get(noUpdate bool) (apps.App, error) { if err != nil { return app, err } - if status == "running" { + if status.Status == "running" { var err error app.Techs, err = container.GetTechs() if err != nil { @@ -304,7 +304,7 @@ func (p *Processor) Delete() error { return err } - if status != "no-container" { + if status.Status != "no-container" { // We stop the container first err = container.Stop() if err != nil { @@ -339,7 +339,7 @@ func (p *Processor) Stop() error { } // Stop the container only when it exists - if status != "no-container" { + if status.Status != "no-container" { err = container.Stop() if err != nil { return err @@ -365,7 +365,7 @@ func (p *Processor) Start() error { if err != nil { return err } - if status == "no-container" { + if status.Status == "no-container" { err = container.Create() if err != nil { return err @@ -547,7 +547,7 @@ func (p *Processor) RestoreFromSnapshot(snapshotName string) error { } // Stop the container only when it exists - if status != "no-container" { + if status.Status != "no-container" { err = container.Stop() if err != nil { return err @@ -565,7 +565,7 @@ func (p *Processor) RestoreFromSnapshot(snapshotName string) error { if err != nil { return err } - if status == "no-container" { + if status.Status == "no-container" { err = container.Create() if err != nil { return err diff --git a/glue/stats.go b/glue/stats.go index 449c81e..72a27ac 100644 --- a/glue/stats.go +++ b/glue/stats.go @@ -52,6 +52,7 @@ func (s *StatsProcessor) UpdateUsage(name string) error { err = processor.UpdateResources( name, state.State, + state.OOMKilled, state.CPUUsage, state.MemoryUsage, state.DiskUsageBytes, @@ -84,7 +85,8 @@ func (s *StatsProcessor) UpdateState(name string) error { err = processor.UpdateState( app.Name, - state, + state.Status, + state.OOMKilled, ) return err } diff --git a/go.mod b/go.mod index beeed4d..ef5e7ac 100644 --- a/go.mod +++ b/go.mod @@ -17,7 +17,6 @@ require ( github.com/kelseyhightower/envconfig v1.4.0 github.com/labstack/echo v3.3.10+incompatible github.com/labstack/gommon v0.3.0 // indirect - github.com/mholt/archiver/v3 v3.5.1 github.com/minio/minio-go/v7 v7.0.14 github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 // indirect github.com/morikuni/aec v1.0.0 // indirect diff --git a/go.sum b/go.sum index c92d9ff..f5c48a9 100644 --- a/go.sum +++ b/go.sum @@ -71,8 +71,6 @@ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuy github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alexflint/go-filemutex v0.0.0-20171022225611-72bdc8eae2ae/go.mod h1:CgnQgUtFrFz9mxFNtED3jI5tLDjKlOM+oUF/sTk6ps0= -github.com/andybalholm/brotli v1.0.1 h1:KqhlKozYbRtJvsPrrEeXcO+N2l6NYT5A2QAFmSULpEc= -github.com/andybalholm/brotli v1.0.1/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= @@ -253,9 +251,6 @@ github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDD github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE= github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= -github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 h1:iFaUwBSo5Svw6L7HYpRu/0lE3e0BaElwnNO1qkNQxBY= -github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5/go.mod h1:qssHWj60/X5sZFNxpG4HBPDHVqxNm4DfnCKgrbZOT+s= -github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= @@ -354,7 +349,6 @@ github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/snappy v0.0.2/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.3 h1:fHPg5GQYlCeLIPB9BZqMVR5nR9A+IM5zcgeTdjMYmLA= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= @@ -446,18 +440,13 @@ github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvW github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/compress v1.11.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= -github.com/klauspost/compress v1.11.4/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.11.13/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.13.4 h1:0zhec2I8zGnjWcKyLl6i3gPqKANCCn5e9xmviEEeX6s= github.com/klauspost/compress v1.13.4/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= -github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/cpuid v1.2.3/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/cpuid v1.3.1 h1:5JNjFYYQrZeKRJ0734q51WCEEn2huer72Dc7K+R/b6s= github.com/klauspost/cpuid v1.3.1/go.mod h1:bYW4mA6ZgKPob1/Dlai2LviZJO7KGI3uoWLd42rAQw4= -github.com/klauspost/pgzip v1.2.5 h1:qnWYvvKqedOF2ulHpMG72XQol4ILEJ8k2wwRl/Km8oE= -github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= @@ -494,8 +483,6 @@ github.com/mattn/go-sqlite3 v1.14.0 h1:mLyGNKR8+Vv9CAU7PphKa2hkEqxxhn8i32J6FPj1/ github.com/mattn/go-sqlite3 v1.14.0/go.mod h1:JIl7NbARA7phWnGvh0LKTyg7S9BA+6gx71ShQilpsus= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= -github.com/mholt/archiver/v3 v3.5.1 h1:rDjOBX9JSF5BvoJGvjqK479aL70qh9DIpZCl+k7Clwo= -github.com/mholt/archiver/v3 v3.5.1/go.mod h1:e3dqJ7H78uzsRSEACH1joayhuSyhnonssnDhppzS1L4= github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= github.com/minio/highwayhash v1.0.1 h1:dZ6IIu8Z14VlC0VpfKofAhCy74wu/Qb5gcn52yWoz/0= github.com/minio/highwayhash v1.0.1/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY= @@ -544,8 +531,6 @@ github.com/nats-io/nkeys v0.3.0/go.mod h1:gvUNGjVcM2IPr5rCsRsC6Wb3Hr2CQAm08dsxtV github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw= github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= github.com/ncw/swift v1.0.47/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM= -github.com/nwaples/rardecode v1.1.0 h1:vSxaY8vQhOcVr4mm5e8XllHWTiM4JF507A0Katqw7MQ= -github.com/nwaples/rardecode v1.1.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= @@ -590,8 +575,6 @@ github.com/opencontainers/selinux v1.8.2/go.mod h1:MUIHuUEvKB1wtJjQdOyYRgOnLD2xA github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= -github.com/pierrec/lz4/v4 v4.1.2 h1:qvY3YFXRQE/XB8MlLzJH7mSzBs74eA2gg52YTk6jUPM= -github.com/pierrec/lz4/v4 v4.1.2/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -697,9 +680,6 @@ github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1 github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= -github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= -github.com/ulikunitz/xz v0.5.9 h1:RsKRIA2MO8x56wkkcd3LbtcE/uMszhb6DpRf+3uwa3I= -github.com/ulikunitz/xz v0.5.9/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= @@ -719,8 +699,6 @@ github.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= github.com/xeipuuv/gojsonschema v0.0.0-20180618132009-1d523034197f/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs= -github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo= -github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=