mirror of
https://github.com/prometheus/alertmanager
synced 2024-12-27 16:42:14 +00:00
Merge pull request #1885 from simonpasquier/bump-prometheus-common
Bump github.com/prometheus/common to v0.4.0
This commit is contained in:
commit
890d20555e
2
go.mod
2
go.mod
@ -22,7 +22,7 @@ require (
|
||||
github.com/oklog/ulid v0.0.0-20170117200651-66bb6560562f
|
||||
github.com/pkg/errors v0.8.0
|
||||
github.com/prometheus/client_golang v0.9.2
|
||||
github.com/prometheus/common v0.2.0
|
||||
github.com/prometheus/common v0.4.0
|
||||
github.com/prometheus/prometheus v0.0.0-20180315085919-58e2a31db8de
|
||||
github.com/rs/cors v1.6.0
|
||||
github.com/satori/go.uuid v0.0.0-20160603004225-b111a074d5ef
|
||||
|
4
go.sum
4
go.sum
@ -143,8 +143,8 @@ github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910 h1:idejC8f
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/common v0.0.0-20181126121408-4724e9255275 h1:PnBWHBf+6L0jOqq0gIVUe6Yk0/QMZ640k6NvkxcBf+8=
|
||||
github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||
github.com/prometheus/common v0.2.0 h1:kUZDBDTdBVBYBj5Tmh2NZLlF60mfjA27rM34b+cVwNU=
|
||||
github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/common v0.4.0 h1:7etb9YClo3a6HjLzfl6rIQaU+FDfi0VSX39io3aQ+DM=
|
||||
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a h1:9a8MnZMP0X2nLJdBg+pBmGgkJlSaKC2KaQmTCk1XDtE=
|
||||
github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
|
19
vendor/github.com/go-openapi/analysis/.golangci.yml
generated
vendored
Normal file
19
vendor/github.com/go-openapi/analysis/.golangci.yml
generated
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
linters-settings:
|
||||
govet:
|
||||
check-shadowing: true
|
||||
golint:
|
||||
min-confidence: 0
|
||||
gocyclo:
|
||||
min-complexity: 30
|
||||
maligned:
|
||||
suggest-new: true
|
||||
dupl:
|
||||
threshold: 100
|
||||
goconst:
|
||||
min-len: 2
|
||||
min-occurrences: 4
|
||||
|
||||
linters:
|
||||
enable-all: true
|
||||
disable:
|
||||
- maligned
|
21
vendor/github.com/go-openapi/spec/.golangci.yml
generated
vendored
Normal file
21
vendor/github.com/go-openapi/spec/.golangci.yml
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
linters-settings:
|
||||
govet:
|
||||
check-shadowing: true
|
||||
golint:
|
||||
min-confidence: 0
|
||||
gocyclo:
|
||||
min-complexity: 25
|
||||
maligned:
|
||||
suggest-new: true
|
||||
dupl:
|
||||
threshold: 100
|
||||
goconst:
|
||||
min-len: 2
|
||||
min-occurrences: 2
|
||||
|
||||
linters:
|
||||
enable-all: true
|
||||
disable:
|
||||
- maligned
|
||||
- unparam
|
||||
- lll
|
19
vendor/github.com/go-openapi/strfmt/.golangci.yml
generated
vendored
Normal file
19
vendor/github.com/go-openapi/strfmt/.golangci.yml
generated
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
linters-settings:
|
||||
govet:
|
||||
check-shadowing: true
|
||||
golint:
|
||||
min-confidence: 0
|
||||
gocyclo:
|
||||
min-complexity: 25
|
||||
maligned:
|
||||
suggest-new: true
|
||||
dupl:
|
||||
threshold: 100
|
||||
goconst:
|
||||
min-len: 2
|
||||
min-occurrences: 2
|
||||
|
||||
linters:
|
||||
enable-all: true
|
||||
disable:
|
||||
- maligned
|
20
vendor/github.com/go-openapi/swag/.golangci.yml
generated
vendored
Normal file
20
vendor/github.com/go-openapi/swag/.golangci.yml
generated
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
linters-settings:
|
||||
govet:
|
||||
check-shadowing: true
|
||||
golint:
|
||||
min-confidence: 0
|
||||
gocyclo:
|
||||
min-complexity: 25
|
||||
maligned:
|
||||
suggest-new: true
|
||||
dupl:
|
||||
threshold: 100
|
||||
goconst:
|
||||
min-len: 3
|
||||
min-occurrences: 2
|
||||
|
||||
linters:
|
||||
enable-all: true
|
||||
disable:
|
||||
- maligned
|
||||
- lll
|
20
vendor/github.com/go-openapi/validate/.golangci.yml
generated
vendored
Normal file
20
vendor/github.com/go-openapi/validate/.golangci.yml
generated
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
linters-settings:
|
||||
govet:
|
||||
check-shadowing: true
|
||||
golint:
|
||||
min-confidence: 0
|
||||
gocyclo:
|
||||
min-complexity: 25
|
||||
maligned:
|
||||
suggest-new: true
|
||||
dupl:
|
||||
threshold: 100
|
||||
goconst:
|
||||
min-len: 2
|
||||
min-occurrences: 2
|
||||
|
||||
linters:
|
||||
enable-all: true
|
||||
disable:
|
||||
- maligned
|
||||
- lll
|
237
vendor/github.com/prometheus/common/config/http_config.go
generated
vendored
237
vendor/github.com/prometheus/common/config/http_config.go
generated
vendored
@ -11,9 +11,13 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// +build go1.8
|
||||
|
||||
package config
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/md5"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
@ -21,12 +25,17 @@ import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/mwitkow/go-conntrack"
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
type closeIdler interface {
|
||||
CloseIdleConnections()
|
||||
}
|
||||
|
||||
// BasicAuth contains basic HTTP authentication credentials.
|
||||
type BasicAuth struct {
|
||||
Username string `yaml:"username"`
|
||||
@ -124,42 +133,53 @@ func NewClientFromConfig(cfg HTTPClientConfig, name string) (*http.Client, error
|
||||
// NewRoundTripperFromConfig returns a new HTTP RoundTripper configured for the
|
||||
// given config.HTTPClientConfig. The name is used as go-conntrack metric label.
|
||||
func NewRoundTripperFromConfig(cfg HTTPClientConfig, name string) (http.RoundTripper, error) {
|
||||
newRT := func(tlsConfig *tls.Config) (http.RoundTripper, error) {
|
||||
// The only timeout we care about is the configured scrape timeout.
|
||||
// It is applied on request. So we leave out any timings here.
|
||||
var rt http.RoundTripper = &http.Transport{
|
||||
Proxy: http.ProxyURL(cfg.ProxyURL.URL),
|
||||
MaxIdleConns: 20000,
|
||||
MaxIdleConnsPerHost: 1000, // see https://github.com/golang/go/issues/13801
|
||||
DisableKeepAlives: false,
|
||||
TLSClientConfig: tlsConfig,
|
||||
DisableCompression: true,
|
||||
// 5 minutes is typically above the maximum sane scrape interval. So we can
|
||||
// use keepalive for all configurations.
|
||||
IdleConnTimeout: 5 * time.Minute,
|
||||
TLSHandshakeTimeout: 10 * time.Second,
|
||||
ExpectContinueTimeout: 1 * time.Second,
|
||||
DialContext: conntrack.NewDialContextFunc(
|
||||
conntrack.DialWithTracing(),
|
||||
conntrack.DialWithName(name),
|
||||
),
|
||||
}
|
||||
|
||||
// If a bearer token is provided, create a round tripper that will set the
|
||||
// Authorization header correctly on each request.
|
||||
if len(cfg.BearerToken) > 0 {
|
||||
rt = NewBearerAuthRoundTripper(cfg.BearerToken, rt)
|
||||
} else if len(cfg.BearerTokenFile) > 0 {
|
||||
rt = NewBearerAuthFileRoundTripper(cfg.BearerTokenFile, rt)
|
||||
}
|
||||
|
||||
if cfg.BasicAuth != nil {
|
||||
rt = NewBasicAuthRoundTripper(cfg.BasicAuth.Username, cfg.BasicAuth.Password, cfg.BasicAuth.PasswordFile, rt)
|
||||
}
|
||||
// Return a new configured RoundTripper.
|
||||
return rt, nil
|
||||
}
|
||||
|
||||
tlsConfig, err := NewTLSConfig(&cfg.TLSConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// The only timeout we care about is the configured scrape timeout.
|
||||
// It is applied on request. So we leave out any timings here.
|
||||
var rt http.RoundTripper = &http.Transport{
|
||||
Proxy: http.ProxyURL(cfg.ProxyURL.URL),
|
||||
MaxIdleConns: 20000,
|
||||
MaxIdleConnsPerHost: 1000, // see https://github.com/golang/go/issues/13801
|
||||
DisableKeepAlives: false,
|
||||
TLSClientConfig: tlsConfig,
|
||||
DisableCompression: true,
|
||||
// 5 minutes is typically above the maximum sane scrape interval. So we can
|
||||
// use keepalive for all configurations.
|
||||
IdleConnTimeout: 5 * time.Minute,
|
||||
DialContext: conntrack.NewDialContextFunc(
|
||||
conntrack.DialWithTracing(),
|
||||
conntrack.DialWithName(name),
|
||||
),
|
||||
|
||||
if len(cfg.TLSConfig.CAFile) == 0 {
|
||||
// No need for a RoundTripper that reloads the CA file automatically.
|
||||
return newRT(tlsConfig)
|
||||
}
|
||||
|
||||
// If a bearer token is provided, create a round tripper that will set the
|
||||
// Authorization header correctly on each request.
|
||||
if len(cfg.BearerToken) > 0 {
|
||||
rt = NewBearerAuthRoundTripper(cfg.BearerToken, rt)
|
||||
} else if len(cfg.BearerTokenFile) > 0 {
|
||||
rt = NewBearerAuthFileRoundTripper(cfg.BearerTokenFile, rt)
|
||||
}
|
||||
|
||||
if cfg.BasicAuth != nil {
|
||||
rt = NewBasicAuthRoundTripper(cfg.BasicAuth.Username, cfg.BasicAuth.Password, cfg.BasicAuth.PasswordFile, rt)
|
||||
}
|
||||
|
||||
// Return a new configured RoundTripper.
|
||||
return rt, nil
|
||||
return newTLSRoundTripper(tlsConfig, cfg.TLSConfig.CAFile, newRT)
|
||||
}
|
||||
|
||||
type bearerAuthRoundTripper struct {
|
||||
@ -181,6 +201,12 @@ func (rt *bearerAuthRoundTripper) RoundTrip(req *http.Request) (*http.Response,
|
||||
return rt.rt.RoundTrip(req)
|
||||
}
|
||||
|
||||
func (rt *bearerAuthRoundTripper) CloseIdleConnections() {
|
||||
if ci, ok := rt.rt.(closeIdler); ok {
|
||||
ci.CloseIdleConnections()
|
||||
}
|
||||
}
|
||||
|
||||
type bearerAuthFileRoundTripper struct {
|
||||
bearerFile string
|
||||
rt http.RoundTripper
|
||||
@ -207,6 +233,12 @@ func (rt *bearerAuthFileRoundTripper) RoundTrip(req *http.Request) (*http.Respon
|
||||
return rt.rt.RoundTrip(req)
|
||||
}
|
||||
|
||||
func (rt *bearerAuthFileRoundTripper) CloseIdleConnections() {
|
||||
if ci, ok := rt.rt.(closeIdler); ok {
|
||||
ci.CloseIdleConnections()
|
||||
}
|
||||
}
|
||||
|
||||
type basicAuthRoundTripper struct {
|
||||
username string
|
||||
password Secret
|
||||
@ -237,6 +269,12 @@ func (rt *basicAuthRoundTripper) RoundTrip(req *http.Request) (*http.Response, e
|
||||
return rt.rt.RoundTrip(req)
|
||||
}
|
||||
|
||||
func (rt *basicAuthRoundTripper) CloseIdleConnections() {
|
||||
if ci, ok := rt.rt.(closeIdler); ok {
|
||||
ci.CloseIdleConnections()
|
||||
}
|
||||
}
|
||||
|
||||
// cloneRequest returns a clone of the provided *http.Request.
|
||||
// The clone is a shallow copy of the struct and its Header map.
|
||||
func cloneRequest(r *http.Request) *http.Request {
|
||||
@ -258,14 +296,13 @@ func NewTLSConfig(cfg *TLSConfig) (*tls.Config, error) {
|
||||
// If a CA cert is provided then let's read it in so we can validate the
|
||||
// scrape target's certificate properly.
|
||||
if len(cfg.CAFile) > 0 {
|
||||
caCertPool := x509.NewCertPool()
|
||||
// Load CA cert.
|
||||
caCert, err := ioutil.ReadFile(cfg.CAFile)
|
||||
b, err := readCAFile(cfg.CAFile)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to use specified CA cert %s: %s", cfg.CAFile, err)
|
||||
return nil, err
|
||||
}
|
||||
if !updateRootCA(tlsConfig, b) {
|
||||
return nil, fmt.Errorf("unable to use specified CA cert %s", cfg.CAFile)
|
||||
}
|
||||
caCertPool.AppendCertsFromPEM(caCert)
|
||||
tlsConfig.RootCAs = caCertPool
|
||||
}
|
||||
|
||||
if len(cfg.ServerName) > 0 {
|
||||
@ -277,13 +314,12 @@ func NewTLSConfig(cfg *TLSConfig) (*tls.Config, error) {
|
||||
} else if len(cfg.KeyFile) > 0 && len(cfg.CertFile) == 0 {
|
||||
return nil, fmt.Errorf("client key file %q specified without client cert file", cfg.KeyFile)
|
||||
} else if len(cfg.CertFile) > 0 && len(cfg.KeyFile) > 0 {
|
||||
cert, err := tls.LoadX509KeyPair(cfg.CertFile, cfg.KeyFile)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to use specified client cert (%s) & key (%s): %s", cfg.CertFile, cfg.KeyFile, err)
|
||||
// Verify that client cert and key are valid.
|
||||
if _, err := cfg.getClientCertificate(nil); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tlsConfig.Certificates = []tls.Certificate{cert}
|
||||
tlsConfig.GetClientCertificate = cfg.getClientCertificate
|
||||
}
|
||||
tlsConfig.BuildNameToCertificate()
|
||||
|
||||
return tlsConfig, nil
|
||||
}
|
||||
@ -308,6 +344,125 @@ func (c *TLSConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||
return unmarshal((*plain)(c))
|
||||
}
|
||||
|
||||
// getClientCertificate reads the pair of client cert and key from disk and returns a tls.Certificate.
|
||||
func (c *TLSConfig) getClientCertificate(*tls.CertificateRequestInfo) (*tls.Certificate, error) {
|
||||
cert, err := tls.LoadX509KeyPair(c.CertFile, c.KeyFile)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to use specified client cert (%s) & key (%s): %s", c.CertFile, c.KeyFile, err)
|
||||
}
|
||||
return &cert, nil
|
||||
}
|
||||
|
||||
// readCAFile reads the CA cert file from disk.
|
||||
func readCAFile(f string) ([]byte, error) {
|
||||
data, err := ioutil.ReadFile(f)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to load specified CA cert %s: %s", f, err)
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// updateRootCA parses the given byte slice as a series of PEM encoded certificates and updates tls.Config.RootCAs.
|
||||
func updateRootCA(cfg *tls.Config, b []byte) bool {
|
||||
caCertPool := x509.NewCertPool()
|
||||
if !caCertPool.AppendCertsFromPEM(b) {
|
||||
return false
|
||||
}
|
||||
cfg.RootCAs = caCertPool
|
||||
return true
|
||||
}
|
||||
|
||||
// tlsRoundTripper is a RoundTripper that updates automatically its TLS
|
||||
// configuration whenever the content of the CA file changes.
|
||||
type tlsRoundTripper struct {
|
||||
caFile string
|
||||
// newRT returns a new RoundTripper.
|
||||
newRT func(*tls.Config) (http.RoundTripper, error)
|
||||
|
||||
mtx sync.RWMutex
|
||||
rt http.RoundTripper
|
||||
hashCAFile []byte
|
||||
tlsConfig *tls.Config
|
||||
}
|
||||
|
||||
func newTLSRoundTripper(
|
||||
cfg *tls.Config,
|
||||
caFile string,
|
||||
newRT func(*tls.Config) (http.RoundTripper, error),
|
||||
) (http.RoundTripper, error) {
|
||||
t := &tlsRoundTripper{
|
||||
caFile: caFile,
|
||||
newRT: newRT,
|
||||
tlsConfig: cfg,
|
||||
}
|
||||
|
||||
rt, err := t.newRT(t.tlsConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
t.rt = rt
|
||||
|
||||
_, t.hashCAFile, err = t.getCAWithHash()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return t, nil
|
||||
}
|
||||
|
||||
func (t *tlsRoundTripper) getCAWithHash() ([]byte, []byte, error) {
|
||||
b, err := readCAFile(t.caFile)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
h := md5.Sum(b)
|
||||
return b, h[:], nil
|
||||
|
||||
}
|
||||
|
||||
// RoundTrip implements the http.RoundTrip interface.
|
||||
func (t *tlsRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
b, h, err := t.getCAWithHash()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
t.mtx.RLock()
|
||||
equal := bytes.Equal(h[:], t.hashCAFile)
|
||||
rt := t.rt
|
||||
t.mtx.RUnlock()
|
||||
if equal {
|
||||
// The CA cert hasn't changed, use the existing RoundTripper.
|
||||
return rt.RoundTrip(req)
|
||||
}
|
||||
|
||||
// Create a new RoundTripper.
|
||||
tlsConfig := t.tlsConfig.Clone()
|
||||
if !updateRootCA(tlsConfig, b) {
|
||||
return nil, fmt.Errorf("unable to use specified CA cert %s", t.caFile)
|
||||
}
|
||||
rt, err = t.newRT(tlsConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
t.CloseIdleConnections()
|
||||
|
||||
t.mtx.Lock()
|
||||
t.rt = rt
|
||||
t.hashCAFile = h[:]
|
||||
t.mtx.Unlock()
|
||||
|
||||
return rt.RoundTrip(req)
|
||||
}
|
||||
|
||||
func (t *tlsRoundTripper) CloseIdleConnections() {
|
||||
t.mtx.RLock()
|
||||
defer t.mtx.RUnlock()
|
||||
if ci, ok := t.rt.(closeIdler); ok {
|
||||
ci.CloseIdleConnections()
|
||||
}
|
||||
}
|
||||
|
||||
func (c HTTPClientConfig) String() string {
|
||||
b, err := yaml.Marshal(c)
|
||||
if err != nil {
|
||||
|
13
vendor/github.com/prometheus/common/promlog/log.go
generated
vendored
13
vendor/github.com/prometheus/common/promlog/log.go
generated
vendored
@ -18,12 +18,23 @@ package promlog
|
||||
|
||||
import (
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/go-kit/kit/log"
|
||||
"github.com/go-kit/kit/log/level"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
var (
|
||||
// This timestamp format differs from RFC3339Nano by using .000 instead
|
||||
// of .999999999 which changes the timestamp from 9 variable to 3 fixed
|
||||
// decimals (.130 instead of .130987456).
|
||||
timestampFormat = log.TimestampFormat(
|
||||
func() time.Time { return time.Now().UTC() },
|
||||
"2006-01-02T15:04:05.000Z07:00",
|
||||
)
|
||||
)
|
||||
|
||||
// AllowedLevel is a settable identifier for the minimum level a log entry
|
||||
// must be have.
|
||||
type AllowedLevel struct {
|
||||
@ -90,6 +101,6 @@ func New(config *Config) log.Logger {
|
||||
}
|
||||
|
||||
l = level.NewFilter(l, config.Level.o)
|
||||
l = log.With(l, "ts", log.DefaultTimestampUTC, "caller", log.DefaultCaller)
|
||||
l = log.With(l, "ts", timestampFormat, "caller", log.DefaultCaller)
|
||||
return l
|
||||
}
|
||||
|
8
vendor/github.com/prometheus/common/route/route.go
generated
vendored
8
vendor/github.com/prometheus/common/route/route.go
generated
vendored
@ -22,9 +22,13 @@ import (
|
||||
|
||||
type param string
|
||||
|
||||
// Param returns param p for the context.
|
||||
// Param returns param p for the context, or the empty string when
|
||||
// param does not exist in context.
|
||||
func Param(ctx context.Context, p string) string {
|
||||
return ctx.Value(param(p)).(string)
|
||||
if v := ctx.Value(param(p)); v != nil {
|
||||
return v.(string)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// WithParam returns a new context with param p set to v.
|
||||
|
2
vendor/modules.txt
vendored
2
vendor/modules.txt
vendored
@ -141,7 +141,7 @@ github.com/prometheus/client_golang/prometheus/promhttp
|
||||
github.com/prometheus/client_golang/prometheus/internal
|
||||
# github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910
|
||||
github.com/prometheus/client_model/go
|
||||
# github.com/prometheus/common v0.2.0
|
||||
# github.com/prometheus/common v0.4.0
|
||||
github.com/prometheus/common/model
|
||||
github.com/prometheus/common/route
|
||||
github.com/prometheus/common/version
|
||||
|
Loading…
Reference in New Issue
Block a user