diff --git a/cmd/cluster-version-operator/start.go b/cmd/cluster-version-operator/start.go index cfc73bc32e..97e0e08188 100644 --- a/cmd/cluster-version-operator/start.go +++ b/cmd/cluster-version-operator/start.go @@ -34,8 +34,6 @@ func init() { cmd.PersistentFlags().StringVar(&opts.NodeName, "node-name", opts.NodeName, "kubernetes node name CVO is scheduled on.") cmd.PersistentFlags().BoolVar(&opts.EnableAutoUpdate, "enable-auto-update", opts.EnableAutoUpdate, "Enables the autoupdate controller.") cmd.PersistentFlags().StringVar(&opts.ReleaseImage, "release-image", opts.ReleaseImage, "The Openshift release image url.") - cmd.PersistentFlags().StringVar(&opts.ServingCertFile, "serving-cert-file", opts.ServingCertFile, "The X.509 certificate file for serving metrics over HTTPS. You must set both --serving-cert-file and --serving-key-file unless you set --listen empty.") - cmd.PersistentFlags().StringVar(&opts.ServingKeyFile, "serving-key-file", opts.ServingKeyFile, "The X.509 key file for serving metrics over HTTPS. You must set both --serving-cert-file and --serving-key-file unless you set --listen empty.") cmd.PersistentFlags().StringVar(&opts.PromQLTarget.CABundleFile, "metrics-ca-bundle-file", opts.PromQLTarget.CABundleFile, "The service CA bundle file containing one or more X.509 certificate files for validating certificates generated from the service CA for the respective remote PromQL query service.") cmd.PersistentFlags().StringVar(&opts.PromQLTarget.BearerTokenFile, "metrics-token-file", opts.PromQLTarget.BearerTokenFile, "The bearer token file used to access the remote PromQL query service.") cmd.PersistentFlags().StringVar(&opts.PromQLTarget.KubeSvc.Namespace, "metrics-namespace", opts.PromQLTarget.KubeSvc.Namespace, "The name of the namespace where the the remote PromQL query service resides. Must be specified when --use-dns-for-services is disabled.") diff --git a/go.mod b/go.mod index dadef1d8f2..1d9b5a4231 100644 --- a/go.mod +++ b/go.mod @@ -25,7 +25,6 @@ require ( golang.org/x/crypto v0.29.0 golang.org/x/net v0.31.0 golang.org/x/time v0.7.0 - gopkg.in/fsnotify.v1 v1.4.7 k8s.io/api v0.32.1 k8s.io/apiextensions-apiserver v0.32.1 k8s.io/apimachinery v0.32.1 @@ -83,7 +82,6 @@ require ( gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/apiserver v0.32.1 // indirect k8s.io/component-base v0.32.1 // indirect k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f // indirect sigs.k8s.io/controller-runtime v0.12.1 // indirect diff --git a/go.sum b/go.sum index d0d30a6f02..8ef210462b 100644 --- a/go.sum +++ b/go.sum @@ -199,8 +199,6 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntN gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4= gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= -gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= -gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= @@ -216,8 +214,6 @@ k8s.io/apiextensions-apiserver v0.32.1 h1:hjkALhRUeCariC8DiVmb5jj0VjIc1N0DREP32+ k8s.io/apiextensions-apiserver v0.32.1/go.mod h1:sxWIGuGiYov7Io1fAS2X06NjMIk5CbRHc2StSmbaQto= k8s.io/apimachinery v0.32.1 h1:683ENpaCBjma4CYqsmZyhEzrGz6cjn1MY/X2jB2hkZs= k8s.io/apimachinery v0.32.1/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE= -k8s.io/apiserver v0.32.1 h1:oo0OozRos66WFq87Zc5tclUX2r0mymoVHRq8JmR7Aak= -k8s.io/apiserver v0.32.1/go.mod h1:UcB9tWjBY7aryeI5zAgzVJB/6k7E97bkr1RgqDz0jPw= k8s.io/client-go v0.32.1 h1:otM0AxdhdBIaQh7l1Q0jQpmo7WOFIk5FFa4bg6YMdUU= k8s.io/client-go v0.32.1/go.mod h1:aTTKZY7MdxUaJ/KiUs8D+GssR9zJZi77ZqtzcGXIiDg= k8s.io/component-base v0.32.1 h1:/5IfJ0dHIKBWysGV0yKTFfacZ5yNV1sulPh3ilJjRZk= diff --git a/install/0000_00_cluster-version-operator_02_secret.yaml b/install/0000_00_cluster-version-operator_02_secret.yaml new file mode 100644 index 0000000000..3eca87352a --- /dev/null +++ b/install/0000_00_cluster-version-operator_02_secret.yaml @@ -0,0 +1,20 @@ +apiVersion: v1 +kind: Secret +metadata: + annotations: + kubernetes.io/description: Secret containing the kube-rbac-proxy configuration. + It allows only HTTPS requests to the /metrics endpoint for the Prometheus + service account. + include.release.openshift.io/self-managed-high-availability: "true" + name: openshift-cluster-version-kube-rbac-proxy-metric + namespace: openshift-cluster-version +stringData: + config.yaml: |- + "authorization": + "static": + - "path": "/metrics" + "resourceRequest": false + "user": + "name": "system:serviceaccount:openshift-monitoring:prometheus-k8s" + "verb": "get" +type: Opaque diff --git a/install/0000_00_cluster-version-operator_03_deployment.yaml b/install/0000_00_cluster-version-operator_03_deployment.yaml index 26dd9f8105..535e471485 100644 --- a/install/0000_00_cluster-version-operator_03_deployment.yaml +++ b/install/0000_00_cluster-version-operator_03_deployment.yaml @@ -24,6 +24,49 @@ spec: spec: automountServiceAccountToken: false containers: + - args: + - --logtostderr + - --secure-listen-address=[$(IP)]:9099 + - --upstream=http://127.0.0.1:9099/ + - --tls-cert-file=/etc/tls/serving-cert/tls.crt + - --tls-private-key-file=/etc/tls/serving-cert/tls.key + - --client-ca-file=/etc/tls/service-ca/service-ca.crt + - --config-file=/etc/kube-rbac-proxy/config.yaml + - --allow-paths=/metrics + env: + - name: IP + valueFrom: + fieldRef: + fieldPath: status.podIP + image: quay.io/brancz/kube-rbac-proxy:v0.13.0 + name: kube-rbac-proxy + ports: + - containerPort: 9099 + hostPort: 9099 + name: metrics + resources: + requests: + cpu: 1m + memory: 15Mi + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + terminationMessagePolicy: FallbackToLogsOnError + volumeMounts: + - mountPath: /etc/tls/serving-cert + name: serving-cert + readOnly: true + - mountPath: /etc/tls/service-ca + name: service-ca + readOnly: true + - mountPath: /etc/kube-rbac-proxy + name: secret-kube-rbac-proxy-metric + readOnly: true + - mountPath: /var/run/secrets/kubernetes.io/serviceaccount + name: kube-api-access + readOnly: true - name: cluster-version-operator image: '{{.ReleaseImage}}' imagePullPolicy: IfNotPresent @@ -31,14 +74,9 @@ spec: - "start" - "--release-image={{.ReleaseImage}}" - "--enable-auto-update=false" - - "--listen=0.0.0.0:9099" - - "--serving-cert-file=/etc/tls/serving-cert/tls.crt" - - "--serving-key-file=/etc/tls/serving-cert/tls.key" + - "--listen=127.0.0.1:9099" - "--v=2" - "--always-enable-capabilities=Ingress" - ports: - - name: metrics - containerPort: 9099 resources: requests: cpu: 20m @@ -51,9 +89,6 @@ spec: - mountPath: /etc/cvo/updatepayloads name: etc-cvo-updatepayloads readOnly: true - - mountPath: /etc/tls/serving-cert - name: serving-cert - readOnly: true - mountPath: /etc/tls/service-ca name: service-ca readOnly: true @@ -101,6 +136,9 @@ spec: effect: "NoExecute" tolerationSeconds: 120 volumes: + - name: secret-kube-rbac-proxy-metric + secret: + secretName: openshift-cluster-version-kube-rbac-proxy-metric - name: etc-ssl-certs hostPath: path: /etc/ssl/certs diff --git a/pkg/cvo/metrics.go b/pkg/cvo/metrics.go index ac2d32fcb8..4e2a56619b 100644 --- a/pkg/cvo/metrics.go +++ b/pkg/cvo/metrics.go @@ -1,17 +1,10 @@ package cvo import ( - "bytes" "context" - "crypto/sha256" - "crypto/tls" - "errors" "fmt" - "io" "net" "net/http" - "os" - "path/filepath" "time" "github.com/prometheus/client_golang/prometheus" @@ -26,9 +19,6 @@ import ( configv1 "github.com/openshift/api/config/v1" "github.com/openshift/cluster-version-operator/lib/resourcemerge" "github.com/openshift/cluster-version-operator/pkg/internal" - "github.com/openshift/library-go/pkg/crypto" - - "gopkg.in/fsnotify.v1" ) // RegisterMetrics initializes metrics and registers them with the @@ -136,28 +126,18 @@ func createHttpServer() *http.Server { return server } -func shutdownHttpServer(parentCtx context.Context, svr *http.Server) { - ctx, cancel := context.WithTimeout(parentCtx, 5*time.Second) - defer cancel() - klog.Info("Shutting down metrics server so it can be recreated with updated TLS configuration.") - if err := svr.Shutdown(ctx); err != nil { - klog.Errorf("Failed to gracefully shut down metrics server during restart: %v", err) - } -} - -func startListening(svr *http.Server, tlsConfig *tls.Config, lAddr string, resultChannel chan asyncResult) { - tcpListener, err := net.Listen("tcp", lAddr) +func startListening(svr *http.Server, lAddr string, resultChannel chan asyncResult) { + listener, err := net.Listen("tcp", lAddr) if err != nil { resultChannel <- asyncResult{ - name: "HTTPS server", + name: "HTTP server", error: fmt.Errorf("failed to listen to the network address %s reserved for cluster-version-operator metrics: %w", lAddr, err), } return } - tlsListener := tls.NewListener(tcpListener, tlsConfig) - klog.Infof("Metrics port listening for HTTPS on %v", lAddr) - err = svr.Serve(tlsListener) - resultChannel <- asyncResult{name: "HTTPS server", error: err} + klog.Infof("Metrics port listening for HTTP on %v", lAddr) + err = svr.Serve(listener) + resultChannel <- asyncResult{name: "HTTP server", error: err} } func handleServerResult(result asyncResult, lastLoopError error) error { @@ -174,61 +154,19 @@ func handleServerResult(result asyncResult, lastLoopError error) error { } // RunMetrics launches a server bound to listenAddress serving -// Prometheus metrics at /metrics over HTTPS. Continues serving +// Prometheus metrics at /metrics over HTTP. Continues serving // until runContext.Done() and then attempts a clean shutdown // limited by shutdownContext.Done(). Assumes runContext.Done() // occurs before or simultaneously with shutdownContext.Done(). -// Also detects changes to metrics certificate files upon which -// the metrics HTTP server is shutdown and recreated with a new -// TLS configuration. -func RunMetrics(runContext context.Context, shutdownContext context.Context, listenAddress, certFile, keyFile string) error { - var tlsConfig *tls.Config - if listenAddress != "" { - var err error - tlsConfig, err = makeTLSConfig(certFile, keyFile) - if err != nil { - return fmt.Errorf("Failed to create TLS config: %w", err) - } - } else { - return errors.New("TLS configuration is required to serve metrics") - } +func RunMetrics(runContext context.Context, shutdownContext context.Context, listenAddress string) error { server := createHttpServer() resultChannel := make(chan asyncResult, 1) resultChannelCount := 1 - go startListening(server, tlsConfig, listenAddress, resultChannel) - - certDir := filepath.Dir(certFile) - keyDir := filepath.Dir(keyFile) - - origCertChecksum, err := checksumFile(certFile) - if err != nil { - return fmt.Errorf("Failed to initialize certificate file checksum: %w", err) - } - origKeyChecksum, err := checksumFile(keyFile) - if err != nil { - return fmt.Errorf("Failed to initialize key file checksum: %w", err) - } - - // Set up and start the file watcher. - watcher, err := fsnotify.NewWatcher() - if watcher == nil || err != nil { - return fmt.Errorf("Failed to create file watcher for certificate and key rotation: %w", err) - } else { - defer watcher.Close() - if err := watcher.Add(certDir); err != nil { - return fmt.Errorf("Failed to add %v to watcher: %w", certDir, err) - } - if certDir != keyDir { - if err := watcher.Add(keyDir); err != nil { - return fmt.Errorf("Failed to add %v to watcher: %w", keyDir, err) - } - } - } + go startListening(server, listenAddress, resultChannel) shutdown := false - restartServer := false var loopError error for resultChannelCount > 0 { if shutdown { @@ -244,53 +182,8 @@ func RunMetrics(runContext context.Context, shutdownContext context.Context, lis select { case <-runContext.Done(): // clean shutdown case result := <-resultChannel: // crashed before a shutdown was requested or metrics server recreated - if restartServer { - klog.Info("Creating metrics server with updated TLS configuration.") - server = createHttpServer() - go startListening(server, tlsConfig, listenAddress, resultChannel) - restartServer = false - continue - } resultChannelCount-- loopError = handleServerResult(result, loopError) - case event := <-watcher.Events: - if event.Op != fsnotify.Chmod && event.Op != fsnotify.Remove { - if changed, err := certsChanged(origCertChecksum, origKeyChecksum, certFile, keyFile); changed { - - // Update file checksums with latest files. - // - if origCertChecksum, err = checksumFile(certFile); err != nil { - klog.Errorf("Failed to update certificate file checksum: %v", err) - loopError = err - break - } - if origKeyChecksum, err = checksumFile(keyFile); err != nil { - klog.Errorf("Failed to update key file checksum: %v", err) - loopError = err - break - } - - tlsConfig, err = makeTLSConfig(certFile, keyFile) - if err == nil { - restartServer = true - shutdownHttpServer(shutdownContext, server) - continue - } else { - klog.Errorf("Failed to create TLS configuration with updated configuration: %v", err) - loopError = err - } - } else if err != nil { - klog.Errorf("%v", err) - loopError = err - } else { - continue - } - } else { - continue - } - case err = <-watcher.Errors: - klog.Errorf("Error from metrics server certificate file watcher: %v", err) - loopError = err } shutdown = true shutdownError := server.Shutdown(shutdownContext) @@ -637,92 +530,3 @@ func mostRecentTimestamp(cv *configv1.ClusterVersion) int64 { } return latest.Unix() } - -// Determine if the certificates have changed and need to be updated. -// If no errors occur, returns true if both files have changed and -// neither is an empty file. Otherwise returns false and any error. -func certsChanged(origCertChecksum []byte, origKeyChecksum []byte, certFile, keyFile string) (bool, error) { - // Check if both files exist. - certNotEmpty, err := fileExistsAndNotEmpty(certFile) - if err != nil { - return false, fmt.Errorf("Error checking if changed TLS cert file empty/exists: %w", err) - } - keyNotEmpty, err := fileExistsAndNotEmpty(keyFile) - if err != nil { - return false, fmt.Errorf("Error checking if changed TLS key file empty/exists: %w", err) - } - if !certNotEmpty || !keyNotEmpty { - // One of the files is missing despite some file event. - return false, fmt.Errorf("Certificate or key is missing or empty, certificates will not be rotated.") - } - - currentCertChecksum, err := checksumFile(certFile) - if err != nil { - return false, fmt.Errorf("Error checking certificate file checksum: %w", err) - } - - currentKeyChecksum, err := checksumFile(keyFile) - if err != nil { - return false, fmt.Errorf("Error checking key file checksum: %w", err) - } - - // Check if the non-empty certificate/key files have actually changed. - if !bytes.Equal(origCertChecksum, currentCertChecksum) && !bytes.Equal(origKeyChecksum, currentKeyChecksum) { - klog.V(2).Info("Certificate and key changed. Will recreate metrics server with updated TLS configuration.") - return true, nil - } - - return false, nil -} - -func makeTLSConfig(servingCertFile, servingKeyFile string) (*tls.Config, error) { - // Load the initial certificate contents. - certBytes, err := os.ReadFile(servingCertFile) - if err != nil { - return nil, err - } - keyBytes, err := os.ReadFile(servingKeyFile) - if err != nil { - return nil, err - } - certificate, err := tls.X509KeyPair(certBytes, keyBytes) - if err != nil { - return nil, err - } - - return crypto.SecureTLSConfig(&tls.Config{ - GetCertificate: func(_ *tls.ClientHelloInfo) (*tls.Certificate, error) { - return &certificate, nil - }, - }), nil -} - -// Compute the sha256 checksum for file 'fName' returning any error. -func checksumFile(fName string) ([]byte, error) { - file, err := os.Open(fName) - if err != nil { - return nil, fmt.Errorf("Failed to open file %v for checksum: %w", fName, err) - } - defer file.Close() - - hash := sha256.New() - - if _, err = io.Copy(hash, file); err != nil { - return nil, fmt.Errorf("Failed to compute checksum for file %v: %w", fName, err) - } - - return hash.Sum(nil), nil -} - -// Check if a file exists and has file.Size() not equal to 0. -// Returns any error returned by os.Stat other than os.ErrNotExist. -func fileExistsAndNotEmpty(fName string) (bool, error) { - if fi, err := os.Stat(fName); err == nil { - return (fi.Size() != 0), nil - } else if errors.Is(err, os.ErrNotExist) { - return false, nil - } else { - // Some other error, file may not exist. - return false, err - } -} diff --git a/pkg/payload/testdata/TestRenderManifest_expected_cvo_deployment.yaml b/pkg/payload/testdata/TestRenderManifest_expected_cvo_deployment.yaml index de44d9bfac..2bb9ac2ab7 100644 --- a/pkg/payload/testdata/TestRenderManifest_expected_cvo_deployment.yaml +++ b/pkg/payload/testdata/TestRenderManifest_expected_cvo_deployment.yaml @@ -24,6 +24,49 @@ spec: spec: automountServiceAccountToken: false containers: + - args: + - --logtostderr + - --secure-listen-address=[$(IP)]:9099 + - --upstream=http://127.0.0.1:9099/ + - --tls-cert-file=/etc/tls/serving-cert/tls.crt + - --tls-private-key-file=/etc/tls/serving-cert/tls.key + - --client-ca-file=/etc/tls/service-ca/service-ca.crt + - --config-file=/etc/kube-rbac-proxy/config.yaml + - --allow-paths=/metrics + env: + - name: IP + valueFrom: + fieldRef: + fieldPath: status.podIP + image: quay.io/brancz/kube-rbac-proxy:v0.13.0 + name: kube-rbac-proxy + ports: + - containerPort: 9099 + hostPort: 9099 + name: metrics + resources: + requests: + cpu: 1m + memory: 15Mi + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + terminationMessagePolicy: FallbackToLogsOnError + volumeMounts: + - mountPath: /etc/tls/serving-cert + name: serving-cert + readOnly: true + - mountPath: /etc/tls/service-ca + name: service-ca + readOnly: true + - mountPath: /etc/kube-rbac-proxy + name: secret-kube-rbac-proxy-metric + readOnly: true + - mountPath: /var/run/secrets/kubernetes.io/serviceaccount + name: kube-api-access + readOnly: true - name: cluster-version-operator image: 'quay.io/cvo/release:latest' imagePullPolicy: IfNotPresent @@ -31,14 +74,9 @@ spec: - "start" - "--release-image=quay.io/cvo/release:latest" - "--enable-auto-update=false" - - "--listen=0.0.0.0:9099" - - "--serving-cert-file=/etc/tls/serving-cert/tls.crt" - - "--serving-key-file=/etc/tls/serving-cert/tls.key" + - "--listen=127.0.0.1:9099" - "--v=2" - "--always-enable-capabilities=Ingress" - ports: - - name: metrics - containerPort: 9099 resources: requests: cpu: 20m @@ -51,9 +89,6 @@ spec: - mountPath: /etc/cvo/updatepayloads name: etc-cvo-updatepayloads readOnly: true - - mountPath: /etc/tls/serving-cert - name: serving-cert - readOnly: true - mountPath: /etc/tls/service-ca name: service-ca readOnly: true @@ -101,6 +136,9 @@ spec: effect: "NoExecute" tolerationSeconds: 120 volumes: + - name: secret-kube-rbac-proxy-metric + secret: + secretName: openshift-cluster-version-kube-rbac-proxy-metric - name: etc-ssl-certs hostPath: path: /etc/ssl/certs diff --git a/pkg/start/start.go b/pkg/start/start.go index 958b827b49..480ebfa6ff 100644 --- a/pkg/start/start.go +++ b/pkg/start/start.go @@ -58,9 +58,7 @@ const ( // Options are the valid inputs to starting the CVO. type Options struct { - ReleaseImage string - ServingCertFile string - ServingKeyFile string + ReleaseImage string Kubeconfig string NodeName string @@ -139,12 +137,6 @@ func (o *Options) ValidateAndComplete() error { if o.ReleaseImage == "" { return fmt.Errorf("missing --release-image flag, it is required") } - if o.ListenAddr != "" && o.ServingCertFile == "" { - return fmt.Errorf("--listen was not set empty, so --serving-cert-file must be set") - } - if o.ListenAddr != "" && o.ServingKeyFile == "" { - return fmt.Errorf("--listen was not set empty, so --serving-key-file must be set") - } if o.PrometheusURLString == "" { return fmt.Errorf("missing --metrics-url flag, it is required") } @@ -357,7 +349,7 @@ func (o *Options) run(ctx context.Context, controllerCtx *Context, lock resource resultChannelCount++ go func() { defer utilruntime.HandleCrash() - err := cvo.RunMetrics(postMainContext, shutdownContext, o.ListenAddr, o.ServingCertFile, o.ServingKeyFile) + err := cvo.RunMetrics(postMainContext, shutdownContext, o.ListenAddr) resultChannel <- asyncResult{name: "metrics server", error: err} }() } diff --git a/vendor/github.com/openshift/library-go/pkg/crypto/OWNERS b/vendor/github.com/openshift/library-go/pkg/crypto/OWNERS deleted file mode 100644 index 4d4ce5ab9e..0000000000 --- a/vendor/github.com/openshift/library-go/pkg/crypto/OWNERS +++ /dev/null @@ -1,4 +0,0 @@ -reviewers: - - stlaz -approvers: - - stlaz diff --git a/vendor/github.com/openshift/library-go/pkg/crypto/crypto.go b/vendor/github.com/openshift/library-go/pkg/crypto/crypto.go deleted file mode 100644 index e6651fecc2..0000000000 --- a/vendor/github.com/openshift/library-go/pkg/crypto/crypto.go +++ /dev/null @@ -1,1221 +0,0 @@ -package crypto - -import ( - "bytes" - "crypto" - "crypto/ecdsa" - "crypto/rand" - "crypto/rsa" - "crypto/sha1" - "crypto/tls" - "crypto/x509" - "crypto/x509/pkix" - "encoding/pem" - "errors" - "fmt" - "io" - "math/big" - mathrand "math/rand" - "net" - "os" - "path/filepath" - "reflect" - "sort" - "strconv" - "sync" - "time" - - "k8s.io/klog/v2" - - "k8s.io/apimachinery/pkg/util/sets" - "k8s.io/apiserver/pkg/authentication/user" - "k8s.io/client-go/util/cert" -) - -// TLS versions that are known to golang. Go 1.13 adds support for -// TLS 1.3 that's opt-out with a build flag. -var versions = map[string]uint16{ - "VersionTLS10": tls.VersionTLS10, - "VersionTLS11": tls.VersionTLS11, - "VersionTLS12": tls.VersionTLS12, - "VersionTLS13": tls.VersionTLS13, -} - -// TLS versions that are enabled. -var supportedVersions = map[string]uint16{ - "VersionTLS10": tls.VersionTLS10, - "VersionTLS11": tls.VersionTLS11, - "VersionTLS12": tls.VersionTLS12, - "VersionTLS13": tls.VersionTLS13, -} - -// TLSVersionToNameOrDie given a tls version as an int, return its readable name -func TLSVersionToNameOrDie(intVal uint16) string { - matches := []string{} - for key, version := range versions { - if version == intVal { - matches = append(matches, key) - } - } - - if len(matches) == 0 { - panic(fmt.Sprintf("no name found for %d", intVal)) - } - if len(matches) > 1 { - panic(fmt.Sprintf("multiple names found for %d: %v", intVal, matches)) - } - return matches[0] -} - -func TLSVersion(versionName string) (uint16, error) { - if len(versionName) == 0 { - return DefaultTLSVersion(), nil - } - if version, ok := versions[versionName]; ok { - return version, nil - } - return 0, fmt.Errorf("unknown tls version %q", versionName) -} -func TLSVersionOrDie(versionName string) uint16 { - version, err := TLSVersion(versionName) - if err != nil { - panic(err) - } - return version -} - -// TLS versions that are known to golang, but may not necessarily be enabled. -func GolangTLSVersions() []string { - supported := []string{} - for k := range versions { - supported = append(supported, k) - } - sort.Strings(supported) - return supported -} - -// Returns the build enabled TLS versions. -func ValidTLSVersions() []string { - validVersions := []string{} - for k := range supportedVersions { - validVersions = append(validVersions, k) - } - sort.Strings(validVersions) - return validVersions -} -func DefaultTLSVersion() uint16 { - // Can't use SSLv3 because of POODLE and BEAST - // Can't use TLSv1.0 because of POODLE and BEAST using CBC cipher - // Can't use TLSv1.1 because of RC4 cipher usage - return tls.VersionTLS12 -} - -// ciphersTLS13 copies golang 1.13 implementation, where TLS1.3 suites are not -// configurable (cipherSuites field is ignored for TLS1.3 flows and all of the -// below three - and none other - are used) -var ciphersTLS13 = map[string]uint16{ - "TLS_AES_128_GCM_SHA256": tls.TLS_AES_128_GCM_SHA256, - "TLS_AES_256_GCM_SHA384": tls.TLS_AES_256_GCM_SHA384, - "TLS_CHACHA20_POLY1305_SHA256": tls.TLS_CHACHA20_POLY1305_SHA256, -} - -var ciphers = map[string]uint16{ - "TLS_RSA_WITH_RC4_128_SHA": tls.TLS_RSA_WITH_RC4_128_SHA, - "TLS_RSA_WITH_3DES_EDE_CBC_SHA": tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA, - "TLS_RSA_WITH_AES_128_CBC_SHA": tls.TLS_RSA_WITH_AES_128_CBC_SHA, - "TLS_RSA_WITH_AES_256_CBC_SHA": tls.TLS_RSA_WITH_AES_256_CBC_SHA, - "TLS_RSA_WITH_AES_128_CBC_SHA256": tls.TLS_RSA_WITH_AES_128_CBC_SHA256, - "TLS_RSA_WITH_AES_128_GCM_SHA256": tls.TLS_RSA_WITH_AES_128_GCM_SHA256, - "TLS_RSA_WITH_AES_256_GCM_SHA384": tls.TLS_RSA_WITH_AES_256_GCM_SHA384, - "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA": tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, - "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, - "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, - "TLS_ECDHE_RSA_WITH_RC4_128_SHA": tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA, - "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, - "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, - "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, - "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256": tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, - "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256": tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, - "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256": tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, - "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256": tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, - "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384": tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, - "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384": tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, - "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305": tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, - "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305": tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, - "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256": tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, - "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256": tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, -} - -// openSSLToIANACiphersMap maps OpenSSL cipher suite names to IANA names -// ref: https://www.iana.org/assignments/tls-parameters/tls-parameters.xml -var openSSLToIANACiphersMap = map[string]string{ - // TLS 1.3 ciphers - not configurable in go 1.13, all of them are used in TLSv1.3 flows - "TLS_AES_128_GCM_SHA256": "TLS_AES_128_GCM_SHA256", // 0x13,0x01 - "TLS_AES_256_GCM_SHA384": "TLS_AES_256_GCM_SHA384", // 0x13,0x02 - "TLS_CHACHA20_POLY1305_SHA256": "TLS_CHACHA20_POLY1305_SHA256", // 0x13,0x03 - - // TLS 1.2 - "ECDHE-ECDSA-AES128-GCM-SHA256": "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", // 0xC0,0x2B - "ECDHE-RSA-AES128-GCM-SHA256": "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", // 0xC0,0x2F - "ECDHE-ECDSA-AES256-GCM-SHA384": "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", // 0xC0,0x2C - "ECDHE-RSA-AES256-GCM-SHA384": "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", // 0xC0,0x30 - "ECDHE-ECDSA-CHACHA20-POLY1305": "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256", // 0xCC,0xA9 - "ECDHE-RSA-CHACHA20-POLY1305": "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256", // 0xCC,0xA8 - "ECDHE-ECDSA-AES128-SHA256": "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256", // 0xC0,0x23 - "ECDHE-RSA-AES128-SHA256": "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", // 0xC0,0x27 - "AES128-GCM-SHA256": "TLS_RSA_WITH_AES_128_GCM_SHA256", // 0x00,0x9C - "AES256-GCM-SHA384": "TLS_RSA_WITH_AES_256_GCM_SHA384", // 0x00,0x9D - "AES128-SHA256": "TLS_RSA_WITH_AES_128_CBC_SHA256", // 0x00,0x3C - - // TLS 1 - "ECDHE-ECDSA-AES128-SHA": "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA", // 0xC0,0x09 - "ECDHE-RSA-AES128-SHA": "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", // 0xC0,0x13 - "ECDHE-ECDSA-AES256-SHA": "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA", // 0xC0,0x0A - "ECDHE-RSA-AES256-SHA": "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", // 0xC0,0x14 - - // SSL 3 - "AES128-SHA": "TLS_RSA_WITH_AES_128_CBC_SHA", // 0x00,0x2F - "AES256-SHA": "TLS_RSA_WITH_AES_256_CBC_SHA", // 0x00,0x35 - "DES-CBC3-SHA": "TLS_RSA_WITH_3DES_EDE_CBC_SHA", // 0x00,0x0A -} - -// CipherSuitesToNamesOrDie given a list of cipher suites as ints, return their readable names -func CipherSuitesToNamesOrDie(intVals []uint16) []string { - ret := []string{} - for _, intVal := range intVals { - ret = append(ret, CipherSuiteToNameOrDie(intVal)) - } - - return ret -} - -// CipherSuiteToNameOrDie given a cipher suite as an int, return its readable name -func CipherSuiteToNameOrDie(intVal uint16) string { - // The following suite ids appear twice in the cipher map (with - // and without the _SHA256 suffix) for the purposes of backwards - // compatibility. Always return the current rather than the legacy - // name. - switch intVal { - case tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256: - return "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256" - case tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256: - return "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256" - } - - matches := []string{} - for key, version := range ciphers { - if version == intVal { - matches = append(matches, key) - } - } - - if len(matches) == 0 { - panic(fmt.Sprintf("no name found for %d", intVal)) - } - if len(matches) > 1 { - panic(fmt.Sprintf("multiple names found for %d: %v", intVal, matches)) - } - return matches[0] -} - -func CipherSuite(cipherName string) (uint16, error) { - if cipher, ok := ciphers[cipherName]; ok { - return cipher, nil - } - - if _, ok := ciphersTLS13[cipherName]; ok { - return 0, fmt.Errorf("all golang TLSv1.3 ciphers are always used for TLSv1.3 flows") - } - - return 0, fmt.Errorf("unknown cipher name %q", cipherName) -} - -func CipherSuitesOrDie(cipherNames []string) []uint16 { - if len(cipherNames) == 0 { - return DefaultCiphers() - } - cipherValues := []uint16{} - for _, cipherName := range cipherNames { - cipher, err := CipherSuite(cipherName) - if err != nil { - panic(err) - } - cipherValues = append(cipherValues, cipher) - } - return cipherValues -} -func ValidCipherSuites() []string { - validCipherSuites := []string{} - for k := range ciphers { - validCipherSuites = append(validCipherSuites, k) - } - sort.Strings(validCipherSuites) - return validCipherSuites -} -func DefaultCiphers() []uint16 { - // HTTP/2 mandates TLS 1.2 or higher with an AEAD cipher - // suite (GCM, Poly1305) and ephemeral key exchange (ECDHE, DHE) for - // perfect forward secrecy. Servers may provide additional cipher - // suites for backwards compatibility with HTTP/1.1 clients. - // See RFC7540, section 9.2 (Use of TLS Features) and Appendix A - // (TLS 1.2 Cipher Suite Black List). - return []uint16{ - tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, - tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, - tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, - tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, // required by http/2 - tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, - tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, - tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, // forbidden by http/2, not flagged by http2isBadCipher() in go1.8 - tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, // forbidden by http/2, not flagged by http2isBadCipher() in go1.8 - tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, // forbidden by http/2 - tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, // forbidden by http/2 - tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, // forbidden by http/2 - tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, // forbidden by http/2 - tls.TLS_RSA_WITH_AES_128_GCM_SHA256, // forbidden by http/2 - tls.TLS_RSA_WITH_AES_256_GCM_SHA384, // forbidden by http/2 - // the next one is in the intermediate suite, but go1.8 http2isBadCipher() complains when it is included at the recommended index - // because it comes after ciphers forbidden by the http/2 spec - // tls.TLS_RSA_WITH_AES_128_CBC_SHA256, - // tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, // forbidden by http/2, disabled to mitigate SWEET32 attack - // tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA, // forbidden by http/2, disabled to mitigate SWEET32 attack - tls.TLS_RSA_WITH_AES_128_CBC_SHA, // forbidden by http/2 - tls.TLS_RSA_WITH_AES_256_CBC_SHA, // forbidden by http/2 - } -} - -// SecureTLSConfig enforces the default minimum security settings for the cluster. -func SecureTLSConfig(config *tls.Config) *tls.Config { - if config.MinVersion == 0 { - config.MinVersion = DefaultTLSVersion() - } - - config.PreferServerCipherSuites = true - if len(config.CipherSuites) == 0 { - config.CipherSuites = DefaultCiphers() - } - return config -} - -// OpenSSLToIANACipherSuites maps input OpenSSL Cipher Suite names to their -// IANA counterparts. -// Unknown ciphers are left out. -func OpenSSLToIANACipherSuites(ciphers []string) []string { - ianaCiphers := make([]string, 0, len(ciphers)) - - for _, c := range ciphers { - ianaCipher, found := openSSLToIANACiphersMap[c] - if found { - ianaCiphers = append(ianaCiphers, ianaCipher) - } - } - - return ianaCiphers -} - -type TLSCertificateConfig struct { - Certs []*x509.Certificate - Key crypto.PrivateKey -} - -type TLSCARoots struct { - Roots []*x509.Certificate -} - -func (c *TLSCertificateConfig) WriteCertConfigFile(certFile, keyFile string) error { - // ensure parent dir - if err := os.MkdirAll(filepath.Dir(certFile), os.FileMode(0755)); err != nil { - return err - } - certFileWriter, err := os.OpenFile(certFile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644) - if err != nil { - return err - } - if err := os.MkdirAll(filepath.Dir(keyFile), os.FileMode(0755)); err != nil { - return err - } - keyFileWriter, err := os.OpenFile(keyFile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) - if err != nil { - return err - } - - if err := writeCertificates(certFileWriter, c.Certs...); err != nil { - return err - } - if err := writeKeyFile(keyFileWriter, c.Key); err != nil { - return err - } - - if err := certFileWriter.Close(); err != nil { - return err - } - if err := keyFileWriter.Close(); err != nil { - return err - } - - return nil -} - -func (c *TLSCertificateConfig) WriteCertConfig(certFile, keyFile io.Writer) error { - if err := writeCertificates(certFile, c.Certs...); err != nil { - return err - } - if err := writeKeyFile(keyFile, c.Key); err != nil { - return err - } - return nil -} - -func (c *TLSCertificateConfig) GetPEMBytes() ([]byte, []byte, error) { - certBytes, err := EncodeCertificates(c.Certs...) - if err != nil { - return nil, nil, err - } - keyBytes, err := EncodeKey(c.Key) - if err != nil { - return nil, nil, err - } - - return certBytes, keyBytes, nil -} - -func GetTLSCertificateConfig(certFile, keyFile string) (*TLSCertificateConfig, error) { - if len(certFile) == 0 { - return nil, errors.New("certFile missing") - } - if len(keyFile) == 0 { - return nil, errors.New("keyFile missing") - } - - certPEMBlock, err := os.ReadFile(certFile) - if err != nil { - return nil, err - } - certs, err := cert.ParseCertsPEM(certPEMBlock) - if err != nil { - return nil, fmt.Errorf("Error reading %s: %s", certFile, err) - } - - keyPEMBlock, err := os.ReadFile(keyFile) - if err != nil { - return nil, err - } - keyPairCert, err := tls.X509KeyPair(certPEMBlock, keyPEMBlock) - if err != nil { - return nil, err - } - key := keyPairCert.PrivateKey - - return &TLSCertificateConfig{certs, key}, nil -} - -func GetTLSCertificateConfigFromBytes(certBytes, keyBytes []byte) (*TLSCertificateConfig, error) { - if len(certBytes) == 0 { - return nil, errors.New("certFile missing") - } - if len(keyBytes) == 0 { - return nil, errors.New("keyFile missing") - } - - certs, err := cert.ParseCertsPEM(certBytes) - if err != nil { - return nil, fmt.Errorf("Error reading cert: %s", err) - } - - keyPairCert, err := tls.X509KeyPair(certBytes, keyBytes) - if err != nil { - return nil, err - } - key := keyPairCert.PrivateKey - - return &TLSCertificateConfig{certs, key}, nil -} - -const ( - DefaultCertificateLifetimeInDays = 365 * 2 // 2 years - DefaultCACertificateLifetimeInDays = 365 * 5 // 5 years - - // Default keys are 2048 bits - keyBits = 2048 -) - -type CA struct { - Config *TLSCertificateConfig - - SerialGenerator SerialGenerator -} - -// SerialGenerator is an interface for getting a serial number for the cert. It MUST be thread-safe. -type SerialGenerator interface { - Next(template *x509.Certificate) (int64, error) -} - -// SerialFileGenerator returns a unique, monotonically increasing serial number and ensures the CA on disk records that value. -type SerialFileGenerator struct { - SerialFile string - - // lock guards access to the Serial field - lock sync.Mutex - Serial int64 -} - -func NewSerialFileGenerator(serialFile string) (*SerialFileGenerator, error) { - // read serial file, it must already exist - serial, err := fileToSerial(serialFile) - if err != nil { - return nil, err - } - - generator := &SerialFileGenerator{ - Serial: serial, - SerialFile: serialFile, - } - - // 0 is unused and 1 is reserved for the CA itself - // Thus we need to guarantee that the first external call to SerialFileGenerator.Next returns 2+ - // meaning that SerialFileGenerator.Serial must not be less than 1 (it is guaranteed to be non-negative) - if generator.Serial < 1 { - // fake a call to Next so the file stays in sync and Serial is incremented - if _, err := generator.Next(&x509.Certificate{}); err != nil { - return nil, err - } - } - - return generator, nil -} - -// Next returns a unique, monotonically increasing serial number and ensures the CA on disk records that value. -func (s *SerialFileGenerator) Next(template *x509.Certificate) (int64, error) { - s.lock.Lock() - defer s.lock.Unlock() - - // do a best effort check to make sure concurrent external writes are not occurring to the underlying serial file - serial, err := fileToSerial(s.SerialFile) - if err != nil { - return 0, err - } - if serial != s.Serial { - return 0, fmt.Errorf("serial file %s out of sync ram=%d disk=%d", s.SerialFile, s.Serial, serial) - } - - next := s.Serial + 1 - s.Serial = next - - // Output in hex, padded to multiples of two characters for OpenSSL's sake - serialText := fmt.Sprintf("%X", next) - if len(serialText)%2 == 1 { - serialText = "0" + serialText - } - // always add a newline at the end to have a valid file - serialText += "\n" - - if err := os.WriteFile(s.SerialFile, []byte(serialText), os.FileMode(0640)); err != nil { - return 0, err - } - return next, nil -} - -func fileToSerial(serialFile string) (int64, error) { - serialData, err := os.ReadFile(serialFile) - if err != nil { - return 0, err - } - - // read the file as a single hex number after stripping any whitespace - serial, err := strconv.ParseInt(string(bytes.TrimSpace(serialData)), 16, 64) - if err != nil { - return 0, err - } - - if serial < 0 { - return 0, fmt.Errorf("invalid negative serial %d in serial file %s", serial, serialFile) - } - - return serial, nil -} - -// RandomSerialGenerator returns a serial based on time.Now and the subject -type RandomSerialGenerator struct { -} - -func (s *RandomSerialGenerator) Next(template *x509.Certificate) (int64, error) { - return randomSerialNumber(), nil -} - -// randomSerialNumber returns a random int64 serial number based on -// time.Now. It is defined separately from the generator interface so -// that the caller doesn't have to worry about an input template or -// error - these are unnecessary when creating a random serial. -func randomSerialNumber() int64 { - r := mathrand.New(mathrand.NewSource(time.Now().UTC().UnixNano())) - return r.Int63() -} - -// EnsureCA returns a CA, whether it was created (as opposed to pre-existing), and any error -// if serialFile is empty, a RandomSerialGenerator will be used -func EnsureCA(certFile, keyFile, serialFile, name string, expireDays int) (*CA, bool, error) { - if ca, err := GetCA(certFile, keyFile, serialFile); err == nil { - return ca, false, err - } - ca, err := MakeSelfSignedCA(certFile, keyFile, serialFile, name, expireDays) - return ca, true, err -} - -// if serialFile is empty, a RandomSerialGenerator will be used -func GetCA(certFile, keyFile, serialFile string) (*CA, error) { - caConfig, err := GetTLSCertificateConfig(certFile, keyFile) - if err != nil { - return nil, err - } - - var serialGenerator SerialGenerator - if len(serialFile) > 0 { - serialGenerator, err = NewSerialFileGenerator(serialFile) - if err != nil { - return nil, err - } - } else { - serialGenerator = &RandomSerialGenerator{} - } - - return &CA{ - SerialGenerator: serialGenerator, - Config: caConfig, - }, nil -} - -func GetCAFromBytes(certBytes, keyBytes []byte) (*CA, error) { - caConfig, err := GetTLSCertificateConfigFromBytes(certBytes, keyBytes) - if err != nil { - return nil, err - } - - return &CA{ - SerialGenerator: &RandomSerialGenerator{}, - Config: caConfig, - }, nil -} - -// if serialFile is empty, a RandomSerialGenerator will be used -func MakeSelfSignedCA(certFile, keyFile, serialFile, name string, expireDays int) (*CA, error) { - klog.V(2).Infof("Generating new CA for %s cert, and key in %s, %s", name, certFile, keyFile) - - caConfig, err := MakeSelfSignedCAConfig(name, expireDays) - if err != nil { - return nil, err - } - if err := caConfig.WriteCertConfigFile(certFile, keyFile); err != nil { - return nil, err - } - - var serialGenerator SerialGenerator - if len(serialFile) > 0 { - // create / overwrite the serial file with a zero padded hex value (ending in a newline to have a valid file) - if err := os.WriteFile(serialFile, []byte("00\n"), 0644); err != nil { - return nil, err - } - serialGenerator, err = NewSerialFileGenerator(serialFile) - if err != nil { - return nil, err - } - } else { - serialGenerator = &RandomSerialGenerator{} - } - - return &CA{ - SerialGenerator: serialGenerator, - Config: caConfig, - }, nil -} - -func MakeSelfSignedCAConfig(name string, expireDays int) (*TLSCertificateConfig, error) { - subject := pkix.Name{CommonName: name} - return MakeSelfSignedCAConfigForSubject(subject, expireDays) -} - -func MakeSelfSignedCAConfigForSubject(subject pkix.Name, expireDays int) (*TLSCertificateConfig, error) { - var caLifetimeInDays = DefaultCACertificateLifetimeInDays - if expireDays > 0 { - caLifetimeInDays = expireDays - } - - if caLifetimeInDays > DefaultCACertificateLifetimeInDays { - warnAboutCertificateLifeTime(subject.CommonName, DefaultCACertificateLifetimeInDays) - } - - caLifetime := time.Duration(caLifetimeInDays) * 24 * time.Hour - return makeSelfSignedCAConfigForSubjectAndDuration(subject, time.Now, caLifetime) -} - -func MakeSelfSignedCAConfigForDuration(name string, caLifetime time.Duration) (*TLSCertificateConfig, error) { - subject := pkix.Name{CommonName: name} - return makeSelfSignedCAConfigForSubjectAndDuration(subject, time.Now, caLifetime) -} - -func UnsafeMakeSelfSignedCAConfigForDurationAtTime(name string, currentTime func() time.Time, caLifetime time.Duration) (*TLSCertificateConfig, error) { - subject := pkix.Name{CommonName: name} - return makeSelfSignedCAConfigForSubjectAndDuration(subject, currentTime, caLifetime) -} - -func makeSelfSignedCAConfigForSubjectAndDuration(subject pkix.Name, currentTime func() time.Time, caLifetime time.Duration) (*TLSCertificateConfig, error) { - // Create CA cert - rootcaPublicKey, rootcaPrivateKey, publicKeyHash, err := newKeyPairWithHash() - if err != nil { - return nil, err - } - // AuthorityKeyId and SubjectKeyId should match for a self-signed CA - authorityKeyId := publicKeyHash - subjectKeyId := publicKeyHash - rootcaTemplate := newSigningCertificateTemplateForDuration(subject, caLifetime, currentTime, authorityKeyId, subjectKeyId) - rootcaCert, err := signCertificate(rootcaTemplate, rootcaPublicKey, rootcaTemplate, rootcaPrivateKey) - if err != nil { - return nil, err - } - caConfig := &TLSCertificateConfig{ - Certs: []*x509.Certificate{rootcaCert}, - Key: rootcaPrivateKey, - } - return caConfig, nil -} - -func MakeCAConfigForDuration(name string, caLifetime time.Duration, issuer *CA) (*TLSCertificateConfig, error) { - // Create CA cert - signerPublicKey, signerPrivateKey, publicKeyHash, err := newKeyPairWithHash() - if err != nil { - return nil, err - } - authorityKeyId := issuer.Config.Certs[0].SubjectKeyId - subjectKeyId := publicKeyHash - signerTemplate := newSigningCertificateTemplateForDuration(pkix.Name{CommonName: name}, caLifetime, time.Now, authorityKeyId, subjectKeyId) - signerCert, err := issuer.SignCertificate(signerTemplate, signerPublicKey) - if err != nil { - return nil, err - } - signerConfig := &TLSCertificateConfig{ - Certs: append([]*x509.Certificate{signerCert}, issuer.Config.Certs...), - Key: signerPrivateKey, - } - return signerConfig, nil -} - -// EnsureSubCA returns a subCA signed by the `ca`, whether it was created -// (as opposed to pre-existing), and any error that might occur during the subCA -// creation. -// If serialFile is an empty string, a RandomSerialGenerator will be used. -func (ca *CA) EnsureSubCA(certFile, keyFile, serialFile, name string, expireDays int) (*CA, bool, error) { - if subCA, err := GetCA(certFile, keyFile, serialFile); err == nil { - return subCA, false, err - } - subCA, err := ca.MakeAndWriteSubCA(certFile, keyFile, serialFile, name, expireDays) - return subCA, true, err -} - -// MakeAndWriteSubCA returns a new sub-CA configuration. New cert/key pair is generated -// while using this function. -// If serialFile is an empty string, a RandomSerialGenerator will be used. -func (ca *CA) MakeAndWriteSubCA(certFile, keyFile, serialFile, name string, expireDays int) (*CA, error) { - klog.V(4).Infof("Generating sub-CA certificate in %s, key in %s, serial in %s", certFile, keyFile, serialFile) - - subCAConfig, err := MakeCAConfigForDuration(name, time.Duration(expireDays)*time.Hour*24, ca) - if err != nil { - return nil, err - } - - if err := subCAConfig.WriteCertConfigFile(certFile, keyFile); err != nil { - return nil, err - } - - var serialGenerator SerialGenerator - if len(serialFile) > 0 { - // create / overwrite the serial file with a zero padded hex value (ending in a newline to have a valid file) - if err := os.WriteFile(serialFile, []byte("00\n"), 0644); err != nil { - return nil, err - } - - serialGenerator, err = NewSerialFileGenerator(serialFile) - if err != nil { - return nil, err - } - } else { - serialGenerator = &RandomSerialGenerator{} - } - - return &CA{ - Config: subCAConfig, - SerialGenerator: serialGenerator, - }, nil -} - -func (ca *CA) EnsureServerCert(certFile, keyFile string, hostnames sets.Set[string], expireDays int) (*TLSCertificateConfig, bool, error) { - certConfig, err := GetServerCert(certFile, keyFile, hostnames) - if err != nil { - certConfig, err = ca.MakeAndWriteServerCert(certFile, keyFile, hostnames, expireDays) - return certConfig, true, err - } - - return certConfig, false, nil -} - -func GetServerCert(certFile, keyFile string, hostnames sets.Set[string]) (*TLSCertificateConfig, error) { - server, err := GetTLSCertificateConfig(certFile, keyFile) - if err != nil { - return nil, err - } - - cert := server.Certs[0] - certNames := sets.New[string]() - for _, ip := range cert.IPAddresses { - certNames.Insert(ip.String()) - } - certNames.Insert(cert.DNSNames...) - if hostnames.Equal(certNames) { - klog.V(4).Infof("Found existing server certificate in %s", certFile) - return server, nil - } - - return nil, fmt.Errorf("Existing server certificate in %s does not match required hostnames.", certFile) -} - -func (ca *CA) MakeAndWriteServerCert(certFile, keyFile string, hostnames sets.Set[string], expireDays int) (*TLSCertificateConfig, error) { - klog.V(4).Infof("Generating server certificate in %s, key in %s", certFile, keyFile) - - server, err := ca.MakeServerCert(hostnames, expireDays) - if err != nil { - return nil, err - } - if err := server.WriteCertConfigFile(certFile, keyFile); err != nil { - return server, err - } - return server, nil -} - -// CertificateExtensionFunc is passed a certificate that it may extend, or return an error -// if the extension attempt failed. -type CertificateExtensionFunc func(*x509.Certificate) error - -func (ca *CA) MakeServerCert(hostnames sets.Set[string], expireDays int, fns ...CertificateExtensionFunc) (*TLSCertificateConfig, error) { - serverPublicKey, serverPrivateKey, publicKeyHash, _ := newKeyPairWithHash() - authorityKeyId := ca.Config.Certs[0].SubjectKeyId - subjectKeyId := publicKeyHash - serverTemplate := newServerCertificateTemplate(pkix.Name{CommonName: sets.List(hostnames)[0]}, sets.List(hostnames), expireDays, time.Now, authorityKeyId, subjectKeyId) - for _, fn := range fns { - if err := fn(serverTemplate); err != nil { - return nil, err - } - } - serverCrt, err := ca.SignCertificate(serverTemplate, serverPublicKey) - if err != nil { - return nil, err - } - server := &TLSCertificateConfig{ - Certs: append([]*x509.Certificate{serverCrt}, ca.Config.Certs...), - Key: serverPrivateKey, - } - return server, nil -} - -func (ca *CA) MakeServerCertForDuration(hostnames sets.Set[string], lifetime time.Duration, fns ...CertificateExtensionFunc) (*TLSCertificateConfig, error) { - serverPublicKey, serverPrivateKey, publicKeyHash, _ := newKeyPairWithHash() - authorityKeyId := ca.Config.Certs[0].SubjectKeyId - subjectKeyId := publicKeyHash - serverTemplate := newServerCertificateTemplateForDuration(pkix.Name{CommonName: sets.List(hostnames)[0]}, sets.List(hostnames), lifetime, time.Now, authorityKeyId, subjectKeyId) - for _, fn := range fns { - if err := fn(serverTemplate); err != nil { - return nil, err - } - } - serverCrt, err := ca.SignCertificate(serverTemplate, serverPublicKey) - if err != nil { - return nil, err - } - server := &TLSCertificateConfig{ - Certs: append([]*x509.Certificate{serverCrt}, ca.Config.Certs...), - Key: serverPrivateKey, - } - return server, nil -} - -func (ca *CA) EnsureClientCertificate(certFile, keyFile string, u user.Info, expireDays int) (*TLSCertificateConfig, bool, error) { - certConfig, err := GetClientCertificate(certFile, keyFile, u) - if err != nil { - certConfig, err = ca.MakeClientCertificate(certFile, keyFile, u, expireDays) - return certConfig, true, err // true indicates we wrote the files. - } - return certConfig, false, nil -} - -func GetClientCertificate(certFile, keyFile string, u user.Info) (*TLSCertificateConfig, error) { - certConfig, err := GetTLSCertificateConfig(certFile, keyFile) - if err != nil { - return nil, err - } - - if subject := certConfig.Certs[0].Subject; subjectChanged(subject, UserToSubject(u)) { - return nil, fmt.Errorf("existing client certificate in %s was issued for a different Subject (%s)", - certFile, subject) - } - - return certConfig, nil -} - -func subjectChanged(existing, expected pkix.Name) bool { - sort.Strings(existing.Organization) - sort.Strings(expected.Organization) - - return existing.CommonName != expected.CommonName || - existing.SerialNumber != expected.SerialNumber || - !reflect.DeepEqual(existing.Organization, expected.Organization) -} - -func (ca *CA) MakeClientCertificate(certFile, keyFile string, u user.Info, expireDays int) (*TLSCertificateConfig, error) { - klog.V(4).Infof("Generating client cert in %s and key in %s", certFile, keyFile) - // ensure parent dirs - if err := os.MkdirAll(filepath.Dir(certFile), os.FileMode(0755)); err != nil { - return nil, err - } - if err := os.MkdirAll(filepath.Dir(keyFile), os.FileMode(0755)); err != nil { - return nil, err - } - - clientPublicKey, clientPrivateKey, _ := NewKeyPair() - clientTemplate := NewClientCertificateTemplate(UserToSubject(u), expireDays, time.Now) - clientCrt, err := ca.SignCertificate(clientTemplate, clientPublicKey) - if err != nil { - return nil, err - } - - certData, err := EncodeCertificates(clientCrt) - if err != nil { - return nil, err - } - keyData, err := EncodeKey(clientPrivateKey) - if err != nil { - return nil, err - } - - if err = os.WriteFile(certFile, certData, os.FileMode(0644)); err != nil { - return nil, err - } - if err = os.WriteFile(keyFile, keyData, os.FileMode(0600)); err != nil { - return nil, err - } - - return GetTLSCertificateConfig(certFile, keyFile) -} - -func (ca *CA) MakeClientCertificateForDuration(u user.Info, lifetime time.Duration) (*TLSCertificateConfig, error) { - clientPublicKey, clientPrivateKey, _ := NewKeyPair() - clientTemplate := NewClientCertificateTemplateForDuration(UserToSubject(u), lifetime, time.Now) - clientCrt, err := ca.SignCertificate(clientTemplate, clientPublicKey) - if err != nil { - return nil, err - } - - certData, err := EncodeCertificates(clientCrt) - if err != nil { - return nil, err - } - keyData, err := EncodeKey(clientPrivateKey) - if err != nil { - return nil, err - } - - return GetTLSCertificateConfigFromBytes(certData, keyData) -} - -type sortedForDER []string - -func (s sortedForDER) Len() int { - return len(s) -} -func (s sortedForDER) Swap(i, j int) { - s[i], s[j] = s[j], s[i] -} -func (s sortedForDER) Less(i, j int) bool { - l1 := len(s[i]) - l2 := len(s[j]) - if l1 == l2 { - return s[i] < s[j] - } - return l1 < l2 -} - -func UserToSubject(u user.Info) pkix.Name { - // Ok we are going to order groups in a peculiar way here to workaround a - // 2 bugs, 1 in golang (https://github.com/golang/go/issues/24254) which - // incorrectly encodes Multivalued RDNs and another in GNUTLS clients - // which are too picky (https://gitlab.com/gnutls/gnutls/issues/403) - // and try to "correct" this issue when reading client certs. - // - // This workaround should be killed once Golang's pkix module is fixed to - // generate a correct DER encoding. - // - // The workaround relies on the fact that the first octect that differs - // between the encoding of two group RDNs will end up being the encoded - // length which is directly related to the group name's length. So we'll - // sort such that shortest names come first. - ugroups := u.GetGroups() - groups := make([]string, len(ugroups)) - copy(groups, ugroups) - sort.Sort(sortedForDER(groups)) - - return pkix.Name{ - CommonName: u.GetName(), - SerialNumber: u.GetUID(), - Organization: groups, - } -} - -func (ca *CA) SignCertificate(template *x509.Certificate, requestKey crypto.PublicKey) (*x509.Certificate, error) { - // Increment and persist serial - serial, err := ca.SerialGenerator.Next(template) - if err != nil { - return nil, err - } - template.SerialNumber = big.NewInt(serial) - return signCertificate(template, requestKey, ca.Config.Certs[0], ca.Config.Key) -} - -func NewKeyPair() (crypto.PublicKey, crypto.PrivateKey, error) { - return newRSAKeyPair() -} - -func newKeyPairWithHash() (crypto.PublicKey, crypto.PrivateKey, []byte, error) { - publicKey, privateKey, err := newRSAKeyPair() - var publicKeyHash []byte - if err == nil { - hash := sha1.New() - hash.Write(publicKey.N.Bytes()) - publicKeyHash = hash.Sum(nil) - } - return publicKey, privateKey, publicKeyHash, err -} - -func newRSAKeyPair() (*rsa.PublicKey, *rsa.PrivateKey, error) { - privateKey, err := rsa.GenerateKey(rand.Reader, keyBits) - if err != nil { - return nil, nil, err - } - return &privateKey.PublicKey, privateKey, nil -} - -// Can be used for CA or intermediate signing certs -func newSigningCertificateTemplateForDuration(subject pkix.Name, caLifetime time.Duration, currentTime func() time.Time, authorityKeyId, subjectKeyId []byte) *x509.Certificate { - return &x509.Certificate{ - Subject: subject, - - SignatureAlgorithm: x509.SHA256WithRSA, - - NotBefore: currentTime().Add(-1 * time.Second), - NotAfter: currentTime().Add(caLifetime), - - // Specify a random serial number to avoid the same issuer+serial - // number referring to different certs in a chain of trust if the - // signing certificate is ever rotated. - SerialNumber: big.NewInt(randomSerialNumber()), - - KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign, - BasicConstraintsValid: true, - IsCA: true, - - AuthorityKeyId: authorityKeyId, - SubjectKeyId: subjectKeyId, - } -} - -// Can be used for ListenAndServeTLS -func newServerCertificateTemplate(subject pkix.Name, hosts []string, expireDays int, currentTime func() time.Time, authorityKeyId, subjectKeyId []byte) *x509.Certificate { - var lifetimeInDays = DefaultCertificateLifetimeInDays - if expireDays > 0 { - lifetimeInDays = expireDays - } - - if lifetimeInDays > DefaultCertificateLifetimeInDays { - warnAboutCertificateLifeTime(subject.CommonName, DefaultCertificateLifetimeInDays) - } - - lifetime := time.Duration(lifetimeInDays) * 24 * time.Hour - - return newServerCertificateTemplateForDuration(subject, hosts, lifetime, currentTime, authorityKeyId, subjectKeyId) -} - -// Can be used for ListenAndServeTLS -func newServerCertificateTemplateForDuration(subject pkix.Name, hosts []string, lifetime time.Duration, currentTime func() time.Time, authorityKeyId, subjectKeyId []byte) *x509.Certificate { - template := &x509.Certificate{ - Subject: subject, - - SignatureAlgorithm: x509.SHA256WithRSA, - - NotBefore: currentTime().Add(-1 * time.Second), - NotAfter: currentTime().Add(lifetime), - SerialNumber: big.NewInt(1), - - KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, - ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, - BasicConstraintsValid: true, - - AuthorityKeyId: authorityKeyId, - SubjectKeyId: subjectKeyId, - } - - template.IPAddresses, template.DNSNames = IPAddressesDNSNames(hosts) - - return template -} - -func IPAddressesDNSNames(hosts []string) ([]net.IP, []string) { - ips := []net.IP{} - dns := []string{} - for _, host := range hosts { - if ip := net.ParseIP(host); ip != nil { - ips = append(ips, ip) - } else { - dns = append(dns, host) - } - } - - // Include IP addresses as DNS subjectAltNames in the cert as well, for the sake of Python, Windows (< 10), and unnamed other libraries - // Ensure these technically invalid DNS subjectAltNames occur after the valid ones, to avoid triggering cert errors in Firefox - // See https://bugzilla.mozilla.org/show_bug.cgi?id=1148766 - for _, ip := range ips { - dns = append(dns, ip.String()) - } - - return ips, dns -} - -func CertsFromPEM(pemCerts []byte) ([]*x509.Certificate, error) { - ok := false - certs := []*x509.Certificate{} - for len(pemCerts) > 0 { - var block *pem.Block - block, pemCerts = pem.Decode(pemCerts) - if block == nil { - break - } - if block.Type != "CERTIFICATE" || len(block.Headers) != 0 { - continue - } - - cert, err := x509.ParseCertificate(block.Bytes) - if err != nil { - return certs, err - } - - certs = append(certs, cert) - ok = true - } - - if !ok { - return certs, errors.New("Could not read any certificates") - } - return certs, nil -} - -// Can be used as a certificate in http.Transport TLSClientConfig -func NewClientCertificateTemplate(subject pkix.Name, expireDays int, currentTime func() time.Time) *x509.Certificate { - var lifetimeInDays = DefaultCertificateLifetimeInDays - if expireDays > 0 { - lifetimeInDays = expireDays - } - - if lifetimeInDays > DefaultCertificateLifetimeInDays { - warnAboutCertificateLifeTime(subject.CommonName, DefaultCertificateLifetimeInDays) - } - - lifetime := time.Duration(lifetimeInDays) * 24 * time.Hour - - return NewClientCertificateTemplateForDuration(subject, lifetime, currentTime) -} - -// Can be used as a certificate in http.Transport TLSClientConfig -func NewClientCertificateTemplateForDuration(subject pkix.Name, lifetime time.Duration, currentTime func() time.Time) *x509.Certificate { - return &x509.Certificate{ - Subject: subject, - - SignatureAlgorithm: x509.SHA256WithRSA, - - NotBefore: currentTime().Add(-1 * time.Second), - NotAfter: currentTime().Add(lifetime), - SerialNumber: big.NewInt(1), - - KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, - ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}, - BasicConstraintsValid: true, - } -} - -func warnAboutCertificateLifeTime(name string, defaultLifetimeInDays int) { - defaultLifetimeInYears := defaultLifetimeInDays / 365 - fmt.Fprintf(os.Stderr, "WARNING: Validity period of the certificate for %q is greater than %d years!\n", name, defaultLifetimeInYears) - fmt.Fprintln(os.Stderr, "WARNING: By security reasons it is strongly recommended to change this period and make it smaller!") -} - -func signCertificate(template *x509.Certificate, requestKey crypto.PublicKey, issuer *x509.Certificate, issuerKey crypto.PrivateKey) (*x509.Certificate, error) { - derBytes, err := x509.CreateCertificate(rand.Reader, template, issuer, requestKey, issuerKey) - if err != nil { - return nil, err - } - certs, err := x509.ParseCertificates(derBytes) - if err != nil { - return nil, err - } - if len(certs) != 1 { - return nil, errors.New("Expected a single certificate") - } - return certs[0], nil -} - -func EncodeCertificates(certs ...*x509.Certificate) ([]byte, error) { - b := bytes.Buffer{} - for _, cert := range certs { - if err := pem.Encode(&b, &pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw}); err != nil { - return []byte{}, err - } - } - return b.Bytes(), nil -} -func EncodeKey(key crypto.PrivateKey) ([]byte, error) { - b := bytes.Buffer{} - switch key := key.(type) { - case *ecdsa.PrivateKey: - keyBytes, err := x509.MarshalECPrivateKey(key) - if err != nil { - return []byte{}, err - } - if err := pem.Encode(&b, &pem.Block{Type: "EC PRIVATE KEY", Bytes: keyBytes}); err != nil { - return b.Bytes(), err - } - case *rsa.PrivateKey: - if err := pem.Encode(&b, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key)}); err != nil { - return []byte{}, err - } - default: - return []byte{}, errors.New("Unrecognized key type") - - } - return b.Bytes(), nil -} - -func writeCertificates(f io.Writer, certs ...*x509.Certificate) error { - bytes, err := EncodeCertificates(certs...) - if err != nil { - return err - } - if _, err := f.Write(bytes); err != nil { - return err - } - - return nil -} -func writeKeyFile(f io.Writer, key crypto.PrivateKey) error { - bytes, err := EncodeKey(key) - if err != nil { - return err - } - if _, err := f.Write(bytes); err != nil { - return err - } - - return nil -} diff --git a/vendor/github.com/openshift/library-go/pkg/crypto/rotation.go b/vendor/github.com/openshift/library-go/pkg/crypto/rotation.go deleted file mode 100644 index 0aa127037c..0000000000 --- a/vendor/github.com/openshift/library-go/pkg/crypto/rotation.go +++ /dev/null @@ -1,20 +0,0 @@ -package crypto - -import ( - "crypto/x509" - "time" -) - -// FilterExpiredCerts checks are all certificates in the bundle valid, i.e. they have not expired. -// The function returns new bundle with only valid certificates or error if no valid certificate is found. -func FilterExpiredCerts(certs ...*x509.Certificate) []*x509.Certificate { - currentTime := time.Now() - var validCerts []*x509.Certificate - for _, c := range certs { - if c.NotAfter.After(currentTime) { - validCerts = append(validCerts, c) - } - } - - return validCerts -} diff --git a/vendor/gopkg.in/fsnotify.v1/.editorconfig b/vendor/gopkg.in/fsnotify.v1/.editorconfig deleted file mode 100644 index ba49e3c234..0000000000 --- a/vendor/gopkg.in/fsnotify.v1/.editorconfig +++ /dev/null @@ -1,5 +0,0 @@ -root = true - -[*] -indent_style = tab -indent_size = 4 diff --git a/vendor/gopkg.in/fsnotify.v1/.gitignore b/vendor/gopkg.in/fsnotify.v1/.gitignore deleted file mode 100644 index 4cd0cbaf43..0000000000 --- a/vendor/gopkg.in/fsnotify.v1/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -# Setup a Global .gitignore for OS and editor generated files: -# https://help.github.com/articles/ignoring-files -# git config --global core.excludesfile ~/.gitignore_global - -.vagrant -*.sublime-project diff --git a/vendor/gopkg.in/fsnotify.v1/.travis.yml b/vendor/gopkg.in/fsnotify.v1/.travis.yml deleted file mode 100644 index 981d1bb813..0000000000 --- a/vendor/gopkg.in/fsnotify.v1/.travis.yml +++ /dev/null @@ -1,30 +0,0 @@ -sudo: false -language: go - -go: - - 1.8.x - - 1.9.x - - tip - -matrix: - allow_failures: - - go: tip - fast_finish: true - -before_script: - - go get -u github.com/golang/lint/golint - -script: - - go test -v --race ./... - -after_script: - - test -z "$(gofmt -s -l -w . | tee /dev/stderr)" - - test -z "$(golint ./... | tee /dev/stderr)" - - go vet ./... - -os: - - linux - - osx - -notifications: - email: false diff --git a/vendor/gopkg.in/fsnotify.v1/AUTHORS b/vendor/gopkg.in/fsnotify.v1/AUTHORS deleted file mode 100644 index 5ab5d41c54..0000000000 --- a/vendor/gopkg.in/fsnotify.v1/AUTHORS +++ /dev/null @@ -1,52 +0,0 @@ -# Names should be added to this file as -# Name or Organization -# The email address is not required for organizations. - -# You can update this list using the following command: -# -# $ git shortlog -se | awk '{print $2 " " $3 " " $4}' - -# Please keep the list sorted. - -Aaron L -Adrien Bustany -Amit Krishnan -Anmol Sethi -Bjørn Erik Pedersen -Bruno Bigras -Caleb Spare -Case Nelson -Chris Howey -Christoffer Buchholz -Daniel Wagner-Hall -Dave Cheney -Evan Phoenix -Francisco Souza -Hari haran -John C Barstow -Kelvin Fo -Ken-ichirou MATSUZAWA -Matt Layher -Nathan Youngman -Nickolai Zeldovich -Patrick -Paul Hammond -Pawel Knap -Pieter Droogendijk -Pursuit92 -Riku Voipio -Rob Figueiredo -Rodrigo Chiossi -Slawek Ligus -Soge Zhang -Tiffany Jernigan -Tilak Sharma -Tom Payne -Travis Cline -Tudor Golubenco -Vahe Khachikyan -Yukang -bronze1man -debrando -henrikedwards -铁哥 diff --git a/vendor/gopkg.in/fsnotify.v1/CHANGELOG.md b/vendor/gopkg.in/fsnotify.v1/CHANGELOG.md deleted file mode 100644 index be4d7ea2c1..0000000000 --- a/vendor/gopkg.in/fsnotify.v1/CHANGELOG.md +++ /dev/null @@ -1,317 +0,0 @@ -# Changelog - -## v1.4.7 / 2018-01-09 - -* BSD/macOS: Fix possible deadlock on closing the watcher on kqueue (thanks @nhooyr and @glycerine) -* Tests: Fix missing verb on format string (thanks @rchiossi) -* Linux: Fix deadlock in Remove (thanks @aarondl) -* Linux: Watch.Add improvements (avoid race, fix consistency, reduce garbage) (thanks @twpayne) -* Docs: Moved FAQ into the README (thanks @vahe) -* Linux: Properly handle inotify's IN_Q_OVERFLOW event (thanks @zeldovich) -* Docs: replace references to OS X with macOS - -## v1.4.2 / 2016-10-10 - -* Linux: use InotifyInit1 with IN_CLOEXEC to stop leaking a file descriptor to a child process when using fork/exec [#178](https://github.com/fsnotify/fsnotify/pull/178) (thanks @pattyshack) - -## v1.4.1 / 2016-10-04 - -* Fix flaky inotify stress test on Linux [#177](https://github.com/fsnotify/fsnotify/pull/177) (thanks @pattyshack) - -## v1.4.0 / 2016-10-01 - -* add a String() method to Event.Op [#165](https://github.com/fsnotify/fsnotify/pull/165) (thanks @oozie) - -## v1.3.1 / 2016-06-28 - -* Windows: fix for double backslash when watching the root of a drive [#151](https://github.com/fsnotify/fsnotify/issues/151) (thanks @brunoqc) - -## v1.3.0 / 2016-04-19 - -* Support linux/arm64 by [patching](https://go-review.googlesource.com/#/c/21971/) x/sys/unix and switching to to it from syscall (thanks @suihkulokki) [#135](https://github.com/fsnotify/fsnotify/pull/135) - -## v1.2.10 / 2016-03-02 - -* Fix golint errors in windows.go [#121](https://github.com/fsnotify/fsnotify/pull/121) (thanks @tiffanyfj) - -## v1.2.9 / 2016-01-13 - -kqueue: Fix logic for CREATE after REMOVE [#111](https://github.com/fsnotify/fsnotify/pull/111) (thanks @bep) - -## v1.2.8 / 2015-12-17 - -* kqueue: fix race condition in Close [#105](https://github.com/fsnotify/fsnotify/pull/105) (thanks @djui for reporting the issue and @ppknap for writing a failing test) -* inotify: fix race in test -* enable race detection for continuous integration (Linux, Mac, Windows) - -## v1.2.5 / 2015-10-17 - -* inotify: use epoll_create1 for arm64 support (requires Linux 2.6.27 or later) [#100](https://github.com/fsnotify/fsnotify/pull/100) (thanks @suihkulokki) -* inotify: fix path leaks [#73](https://github.com/fsnotify/fsnotify/pull/73) (thanks @chamaken) -* kqueue: watch for rename events on subdirectories [#83](https://github.com/fsnotify/fsnotify/pull/83) (thanks @guotie) -* kqueue: avoid infinite loops from symlinks cycles [#101](https://github.com/fsnotify/fsnotify/pull/101) (thanks @illicitonion) - -## v1.2.1 / 2015-10-14 - -* kqueue: don't watch named pipes [#98](https://github.com/fsnotify/fsnotify/pull/98) (thanks @evanphx) - -## v1.2.0 / 2015-02-08 - -* inotify: use epoll to wake up readEvents [#66](https://github.com/fsnotify/fsnotify/pull/66) (thanks @PieterD) -* inotify: closing watcher should now always shut down goroutine [#63](https://github.com/fsnotify/fsnotify/pull/63) (thanks @PieterD) -* kqueue: close kqueue after removing watches, fixes [#59](https://github.com/fsnotify/fsnotify/issues/59) - -## v1.1.1 / 2015-02-05 - -* inotify: Retry read on EINTR [#61](https://github.com/fsnotify/fsnotify/issues/61) (thanks @PieterD) - -## v1.1.0 / 2014-12-12 - -* kqueue: rework internals [#43](https://github.com/fsnotify/fsnotify/pull/43) - * add low-level functions - * only need to store flags on directories - * less mutexes [#13](https://github.com/fsnotify/fsnotify/issues/13) - * done can be an unbuffered channel - * remove calls to os.NewSyscallError -* More efficient string concatenation for Event.String() [#52](https://github.com/fsnotify/fsnotify/pull/52) (thanks @mdlayher) -* kqueue: fix regression in rework causing subdirectories to be watched [#48](https://github.com/fsnotify/fsnotify/issues/48) -* kqueue: cleanup internal watch before sending remove event [#51](https://github.com/fsnotify/fsnotify/issues/51) - -## v1.0.4 / 2014-09-07 - -* kqueue: add dragonfly to the build tags. -* Rename source code files, rearrange code so exported APIs are at the top. -* Add done channel to example code. [#37](https://github.com/fsnotify/fsnotify/pull/37) (thanks @chenyukang) - -## v1.0.3 / 2014-08-19 - -* [Fix] Windows MOVED_TO now translates to Create like on BSD and Linux. [#36](https://github.com/fsnotify/fsnotify/issues/36) - -## v1.0.2 / 2014-08-17 - -* [Fix] Missing create events on macOS. [#14](https://github.com/fsnotify/fsnotify/issues/14) (thanks @zhsso) -* [Fix] Make ./path and path equivalent. (thanks @zhsso) - -## v1.0.0 / 2014-08-15 - -* [API] Remove AddWatch on Windows, use Add. -* Improve documentation for exported identifiers. [#30](https://github.com/fsnotify/fsnotify/issues/30) -* Minor updates based on feedback from golint. - -## dev / 2014-07-09 - -* Moved to [github.com/fsnotify/fsnotify](https://github.com/fsnotify/fsnotify). -* Use os.NewSyscallError instead of returning errno (thanks @hariharan-uno) - -## dev / 2014-07-04 - -* kqueue: fix incorrect mutex used in Close() -* Update example to demonstrate usage of Op. - -## dev / 2014-06-28 - -* [API] Don't set the Write Op for attribute notifications [#4](https://github.com/fsnotify/fsnotify/issues/4) -* Fix for String() method on Event (thanks Alex Brainman) -* Don't build on Plan 9 or Solaris (thanks @4ad) - -## dev / 2014-06-21 - -* Events channel of type Event rather than *Event. -* [internal] use syscall constants directly for inotify and kqueue. -* [internal] kqueue: rename events to kevents and fileEvent to event. - -## dev / 2014-06-19 - -* Go 1.3+ required on Windows (uses syscall.ERROR_MORE_DATA internally). -* [internal] remove cookie from Event struct (unused). -* [internal] Event struct has the same definition across every OS. -* [internal] remove internal watch and removeWatch methods. - -## dev / 2014-06-12 - -* [API] Renamed Watch() to Add() and RemoveWatch() to Remove(). -* [API] Pluralized channel names: Events and Errors. -* [API] Renamed FileEvent struct to Event. -* [API] Op constants replace methods like IsCreate(). - -## dev / 2014-06-12 - -* Fix data race on kevent buffer (thanks @tilaks) [#98](https://github.com/howeyc/fsnotify/pull/98) - -## dev / 2014-05-23 - -* [API] Remove current implementation of WatchFlags. - * current implementation doesn't take advantage of OS for efficiency - * provides little benefit over filtering events as they are received, but has extra bookkeeping and mutexes - * no tests for the current implementation - * not fully implemented on Windows [#93](https://github.com/howeyc/fsnotify/issues/93#issuecomment-39285195) - -## v0.9.3 / 2014-12-31 - -* kqueue: cleanup internal watch before sending remove event [#51](https://github.com/fsnotify/fsnotify/issues/51) - -## v0.9.2 / 2014-08-17 - -* [Backport] Fix missing create events on macOS. [#14](https://github.com/fsnotify/fsnotify/issues/14) (thanks @zhsso) - -## v0.9.1 / 2014-06-12 - -* Fix data race on kevent buffer (thanks @tilaks) [#98](https://github.com/howeyc/fsnotify/pull/98) - -## v0.9.0 / 2014-01-17 - -* IsAttrib() for events that only concern a file's metadata [#79][] (thanks @abustany) -* [Fix] kqueue: fix deadlock [#77][] (thanks @cespare) -* [NOTICE] Development has moved to `code.google.com/p/go.exp/fsnotify` in preparation for inclusion in the Go standard library. - -## v0.8.12 / 2013-11-13 - -* [API] Remove FD_SET and friends from Linux adapter - -## v0.8.11 / 2013-11-02 - -* [Doc] Add Changelog [#72][] (thanks @nathany) -* [Doc] Spotlight and double modify events on macOS [#62][] (reported by @paulhammond) - -## v0.8.10 / 2013-10-19 - -* [Fix] kqueue: remove file watches when parent directory is removed [#71][] (reported by @mdwhatcott) -* [Fix] kqueue: race between Close and readEvents [#70][] (reported by @bernerdschaefer) -* [Doc] specify OS-specific limits in README (thanks @debrando) - -## v0.8.9 / 2013-09-08 - -* [Doc] Contributing (thanks @nathany) -* [Doc] update package path in example code [#63][] (thanks @paulhammond) -* [Doc] GoCI badge in README (Linux only) [#60][] -* [Doc] Cross-platform testing with Vagrant [#59][] (thanks @nathany) - -## v0.8.8 / 2013-06-17 - -* [Fix] Windows: handle `ERROR_MORE_DATA` on Windows [#49][] (thanks @jbowtie) - -## v0.8.7 / 2013-06-03 - -* [API] Make syscall flags internal -* [Fix] inotify: ignore event changes -* [Fix] race in symlink test [#45][] (reported by @srid) -* [Fix] tests on Windows -* lower case error messages - -## v0.8.6 / 2013-05-23 - -* kqueue: Use EVT_ONLY flag on Darwin -* [Doc] Update README with full example - -## v0.8.5 / 2013-05-09 - -* [Fix] inotify: allow monitoring of "broken" symlinks (thanks @tsg) - -## v0.8.4 / 2013-04-07 - -* [Fix] kqueue: watch all file events [#40][] (thanks @ChrisBuchholz) - -## v0.8.3 / 2013-03-13 - -* [Fix] inoitfy/kqueue memory leak [#36][] (reported by @nbkolchin) -* [Fix] kqueue: use fsnFlags for watching a directory [#33][] (reported by @nbkolchin) - -## v0.8.2 / 2013-02-07 - -* [Doc] add Authors -* [Fix] fix data races for map access [#29][] (thanks @fsouza) - -## v0.8.1 / 2013-01-09 - -* [Fix] Windows path separators -* [Doc] BSD License - -## v0.8.0 / 2012-11-09 - -* kqueue: directory watching improvements (thanks @vmirage) -* inotify: add `IN_MOVED_TO` [#25][] (requested by @cpisto) -* [Fix] kqueue: deleting watched directory [#24][] (reported by @jakerr) - -## v0.7.4 / 2012-10-09 - -* [Fix] inotify: fixes from https://codereview.appspot.com/5418045/ (ugorji) -* [Fix] kqueue: preserve watch flags when watching for delete [#21][] (reported by @robfig) -* [Fix] kqueue: watch the directory even if it isn't a new watch (thanks @robfig) -* [Fix] kqueue: modify after recreation of file - -## v0.7.3 / 2012-09-27 - -* [Fix] kqueue: watch with an existing folder inside the watched folder (thanks @vmirage) -* [Fix] kqueue: no longer get duplicate CREATE events - -## v0.7.2 / 2012-09-01 - -* kqueue: events for created directories - -## v0.7.1 / 2012-07-14 - -* [Fix] for renaming files - -## v0.7.0 / 2012-07-02 - -* [Feature] FSNotify flags -* [Fix] inotify: Added file name back to event path - -## v0.6.0 / 2012-06-06 - -* kqueue: watch files after directory created (thanks @tmc) - -## v0.5.1 / 2012-05-22 - -* [Fix] inotify: remove all watches before Close() - -## v0.5.0 / 2012-05-03 - -* [API] kqueue: return errors during watch instead of sending over channel -* kqueue: match symlink behavior on Linux -* inotify: add `DELETE_SELF` (requested by @taralx) -* [Fix] kqueue: handle EINTR (reported by @robfig) -* [Doc] Godoc example [#1][] (thanks @davecheney) - -## v0.4.0 / 2012-03-30 - -* Go 1 released: build with go tool -* [Feature] Windows support using winfsnotify -* Windows does not have attribute change notifications -* Roll attribute notifications into IsModify - -## v0.3.0 / 2012-02-19 - -* kqueue: add files when watch directory - -## v0.2.0 / 2011-12-30 - -* update to latest Go weekly code - -## v0.1.0 / 2011-10-19 - -* kqueue: add watch on file creation to match inotify -* kqueue: create file event -* inotify: ignore `IN_IGNORED` events -* event String() -* linux: common FileEvent functions -* initial commit - -[#79]: https://github.com/howeyc/fsnotify/pull/79 -[#77]: https://github.com/howeyc/fsnotify/pull/77 -[#72]: https://github.com/howeyc/fsnotify/issues/72 -[#71]: https://github.com/howeyc/fsnotify/issues/71 -[#70]: https://github.com/howeyc/fsnotify/issues/70 -[#63]: https://github.com/howeyc/fsnotify/issues/63 -[#62]: https://github.com/howeyc/fsnotify/issues/62 -[#60]: https://github.com/howeyc/fsnotify/issues/60 -[#59]: https://github.com/howeyc/fsnotify/issues/59 -[#49]: https://github.com/howeyc/fsnotify/issues/49 -[#45]: https://github.com/howeyc/fsnotify/issues/45 -[#40]: https://github.com/howeyc/fsnotify/issues/40 -[#36]: https://github.com/howeyc/fsnotify/issues/36 -[#33]: https://github.com/howeyc/fsnotify/issues/33 -[#29]: https://github.com/howeyc/fsnotify/issues/29 -[#25]: https://github.com/howeyc/fsnotify/issues/25 -[#24]: https://github.com/howeyc/fsnotify/issues/24 -[#21]: https://github.com/howeyc/fsnotify/issues/21 diff --git a/vendor/gopkg.in/fsnotify.v1/CONTRIBUTING.md b/vendor/gopkg.in/fsnotify.v1/CONTRIBUTING.md deleted file mode 100644 index 828a60b24b..0000000000 --- a/vendor/gopkg.in/fsnotify.v1/CONTRIBUTING.md +++ /dev/null @@ -1,77 +0,0 @@ -# Contributing - -## Issues - -* Request features and report bugs using the [GitHub Issue Tracker](https://github.com/fsnotify/fsnotify/issues). -* Please indicate the platform you are using fsnotify on. -* A code example to reproduce the problem is appreciated. - -## Pull Requests - -### Contributor License Agreement - -fsnotify is derived from code in the [golang.org/x/exp](https://godoc.org/golang.org/x/exp) package and it may be included [in the standard library](https://github.com/fsnotify/fsnotify/issues/1) in the future. Therefore fsnotify carries the same [LICENSE](https://github.com/fsnotify/fsnotify/blob/master/LICENSE) as Go. Contributors retain their copyright, so you need to fill out a short form before we can accept your contribution: [Google Individual Contributor License Agreement](https://developers.google.com/open-source/cla/individual). - -Please indicate that you have signed the CLA in your pull request. - -### How fsnotify is Developed - -* Development is done on feature branches. -* Tests are run on BSD, Linux, macOS and Windows. -* Pull requests are reviewed and [applied to master][am] using [hub][]. - * Maintainers may modify or squash commits rather than asking contributors to. -* To issue a new release, the maintainers will: - * Update the CHANGELOG - * Tag a version, which will become available through gopkg.in. - -### How to Fork - -For smooth sailing, always use the original import path. Installing with `go get` makes this easy. - -1. Install from GitHub (`go get -u github.com/fsnotify/fsnotify`) -2. Create your feature branch (`git checkout -b my-new-feature`) -3. Ensure everything works and the tests pass (see below) -4. Commit your changes (`git commit -am 'Add some feature'`) - -Contribute upstream: - -1. Fork fsnotify on GitHub -2. Add your remote (`git remote add fork git@github.com:mycompany/repo.git`) -3. Push to the branch (`git push fork my-new-feature`) -4. Create a new Pull Request on GitHub - -This workflow is [thoroughly explained by Katrina Owen](https://splice.com/blog/contributing-open-source-git-repositories-go/). - -### Testing - -fsnotify uses build tags to compile different code on Linux, BSD, macOS, and Windows. - -Before doing a pull request, please do your best to test your changes on multiple platforms, and list which platforms you were able/unable to test on. - -To aid in cross-platform testing there is a Vagrantfile for Linux and BSD. - -* Install [Vagrant](http://www.vagrantup.com/) and [VirtualBox](https://www.virtualbox.org/) -* Setup [Vagrant Gopher](https://github.com/nathany/vagrant-gopher) in your `src` folder. -* Run `vagrant up` from the project folder. You can also setup just one box with `vagrant up linux` or `vagrant up bsd` (note: the BSD box doesn't support Windows hosts at this time, and NFS may prompt for your host OS password) -* Once setup, you can run the test suite on a given OS with a single command `vagrant ssh linux -c 'cd fsnotify/fsnotify; go test'`. -* When you're done, you will want to halt or destroy the Vagrant boxes. - -Notice: fsnotify file system events won't trigger in shared folders. The tests get around this limitation by using the /tmp directory. - -Right now there is no equivalent solution for Windows and macOS, but there are Windows VMs [freely available from Microsoft](http://www.modern.ie/en-us/virtualization-tools#downloads). - -### Maintainers - -Help maintaining fsnotify is welcome. To be a maintainer: - -* Submit a pull request and sign the CLA as above. -* You must be able to run the test suite on Mac, Windows, Linux and BSD. - -To keep master clean, the fsnotify project uses the "apply mail" workflow outlined in Nathaniel Talbott's post ["Merge pull request" Considered Harmful][am]. This requires installing [hub][]. - -All code changes should be internal pull requests. - -Releases are tagged using [Semantic Versioning](http://semver.org/). - -[hub]: https://github.com/github/hub -[am]: http://blog.spreedly.com/2014/06/24/merge-pull-request-considered-harmful/#.VGa5yZPF_Zs diff --git a/vendor/gopkg.in/fsnotify.v1/LICENSE b/vendor/gopkg.in/fsnotify.v1/LICENSE deleted file mode 100644 index f21e540800..0000000000 --- a/vendor/gopkg.in/fsnotify.v1/LICENSE +++ /dev/null @@ -1,28 +0,0 @@ -Copyright (c) 2012 The Go Authors. All rights reserved. -Copyright (c) 2012 fsnotify Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/gopkg.in/fsnotify.v1/README.md b/vendor/gopkg.in/fsnotify.v1/README.md deleted file mode 100644 index 3993207413..0000000000 --- a/vendor/gopkg.in/fsnotify.v1/README.md +++ /dev/null @@ -1,79 +0,0 @@ -# File system notifications for Go - -[![GoDoc](https://godoc.org/github.com/fsnotify/fsnotify?status.svg)](https://godoc.org/github.com/fsnotify/fsnotify) [![Go Report Card](https://goreportcard.com/badge/github.com/fsnotify/fsnotify)](https://goreportcard.com/report/github.com/fsnotify/fsnotify) - -fsnotify utilizes [golang.org/x/sys](https://godoc.org/golang.org/x/sys) rather than `syscall` from the standard library. Ensure you have the latest version installed by running: - -```console -go get -u golang.org/x/sys/... -``` - -Cross platform: Windows, Linux, BSD and macOS. - -|Adapter |OS |Status | -|----------|----------|----------| -|inotify |Linux 2.6.27 or later, Android\*|Supported [![Build Status](https://travis-ci.org/fsnotify/fsnotify.svg?branch=master)](https://travis-ci.org/fsnotify/fsnotify)| -|kqueue |BSD, macOS, iOS\*|Supported [![Build Status](https://travis-ci.org/fsnotify/fsnotify.svg?branch=master)](https://travis-ci.org/fsnotify/fsnotify)| -|ReadDirectoryChangesW|Windows|Supported [![Build status](https://ci.appveyor.com/api/projects/status/ivwjubaih4r0udeh/branch/master?svg=true)](https://ci.appveyor.com/project/NathanYoungman/fsnotify/branch/master)| -|FSEvents |macOS |[Planned](https://github.com/fsnotify/fsnotify/issues/11)| -|FEN |Solaris 11 |[In Progress](https://github.com/fsnotify/fsnotify/issues/12)| -|fanotify |Linux 2.6.37+ | | -|USN Journals |Windows |[Maybe](https://github.com/fsnotify/fsnotify/issues/53)| -|Polling |*All* |[Maybe](https://github.com/fsnotify/fsnotify/issues/9)| - -\* Android and iOS are untested. - -Please see [the documentation](https://godoc.org/github.com/fsnotify/fsnotify) and consult the [FAQ](#faq) for usage information. - -## API stability - -fsnotify is a fork of [howeyc/fsnotify](https://godoc.org/github.com/howeyc/fsnotify) with a new API as of v1.0. The API is based on [this design document](http://goo.gl/MrYxyA). - -All [releases](https://github.com/fsnotify/fsnotify/releases) are tagged based on [Semantic Versioning](http://semver.org/). Further API changes are [planned](https://github.com/fsnotify/fsnotify/milestones), and will be tagged with a new major revision number. - -Go 1.6 supports dependencies located in the `vendor/` folder. Unless you are creating a library, it is recommended that you copy fsnotify into `vendor/github.com/fsnotify/fsnotify` within your project, and likewise for `golang.org/x/sys`. - -## Contributing - -Please refer to [CONTRIBUTING][] before opening an issue or pull request. - -## Example - -See [example_test.go](https://github.com/fsnotify/fsnotify/blob/master/example_test.go). - -## FAQ - -**When a file is moved to another directory is it still being watched?** - -No (it shouldn't be, unless you are watching where it was moved to). - -**When I watch a directory, are all subdirectories watched as well?** - -No, you must add watches for any directory you want to watch (a recursive watcher is on the roadmap [#18][]). - -**Do I have to watch the Error and Event channels in a separate goroutine?** - -As of now, yes. Looking into making this single-thread friendly (see [howeyc #7][#7]) - -**Why am I receiving multiple events for the same file on OS X?** - -Spotlight indexing on OS X can result in multiple events (see [howeyc #62][#62]). A temporary workaround is to add your folder(s) to the *Spotlight Privacy settings* until we have a native FSEvents implementation (see [#11][]). - -**How many files can be watched at once?** - -There are OS-specific limits as to how many watches can be created: -* Linux: /proc/sys/fs/inotify/max_user_watches contains the limit, reaching this limit results in a "no space left on device" error. -* BSD / OSX: sysctl variables "kern.maxfiles" and "kern.maxfilesperproc", reaching these limits results in a "too many open files" error. - -[#62]: https://github.com/howeyc/fsnotify/issues/62 -[#18]: https://github.com/fsnotify/fsnotify/issues/18 -[#11]: https://github.com/fsnotify/fsnotify/issues/11 -[#7]: https://github.com/howeyc/fsnotify/issues/7 - -[contributing]: https://github.com/fsnotify/fsnotify/blob/master/CONTRIBUTING.md - -## Related Projects - -* [notify](https://github.com/rjeczalik/notify) -* [fsevents](https://github.com/fsnotify/fsevents) - diff --git a/vendor/gopkg.in/fsnotify.v1/fen.go b/vendor/gopkg.in/fsnotify.v1/fen.go deleted file mode 100644 index ced39cb881..0000000000 --- a/vendor/gopkg.in/fsnotify.v1/fen.go +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build solaris - -package fsnotify - -import ( - "errors" -) - -// Watcher watches a set of files, delivering events to a channel. -type Watcher struct { - Events chan Event - Errors chan error -} - -// NewWatcher establishes a new watcher with the underlying OS and begins waiting for events. -func NewWatcher() (*Watcher, error) { - return nil, errors.New("FEN based watcher not yet supported for fsnotify\n") -} - -// Close removes all watches and closes the events channel. -func (w *Watcher) Close() error { - return nil -} - -// Add starts watching the named file or directory (non-recursively). -func (w *Watcher) Add(name string) error { - return nil -} - -// Remove stops watching the the named file or directory (non-recursively). -func (w *Watcher) Remove(name string) error { - return nil -} diff --git a/vendor/gopkg.in/fsnotify.v1/fsnotify.go b/vendor/gopkg.in/fsnotify.v1/fsnotify.go deleted file mode 100644 index 190bf0de57..0000000000 --- a/vendor/gopkg.in/fsnotify.v1/fsnotify.go +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright 2012 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build !plan9 - -// Package fsnotify provides a platform-independent interface for file system notifications. -package fsnotify - -import ( - "bytes" - "errors" - "fmt" -) - -// Event represents a single file system notification. -type Event struct { - Name string // Relative path to the file or directory. - Op Op // File operation that triggered the event. -} - -// Op describes a set of file operations. -type Op uint32 - -// These are the generalized file operations that can trigger a notification. -const ( - Create Op = 1 << iota - Write - Remove - Rename - Chmod -) - -func (op Op) String() string { - // Use a buffer for efficient string concatenation - var buffer bytes.Buffer - - if op&Create == Create { - buffer.WriteString("|CREATE") - } - if op&Remove == Remove { - buffer.WriteString("|REMOVE") - } - if op&Write == Write { - buffer.WriteString("|WRITE") - } - if op&Rename == Rename { - buffer.WriteString("|RENAME") - } - if op&Chmod == Chmod { - buffer.WriteString("|CHMOD") - } - if buffer.Len() == 0 { - return "" - } - return buffer.String()[1:] // Strip leading pipe -} - -// String returns a string representation of the event in the form -// "file: REMOVE|WRITE|..." -func (e Event) String() string { - return fmt.Sprintf("%q: %s", e.Name, e.Op.String()) -} - -// Common errors that can be reported by a watcher -var ErrEventOverflow = errors.New("fsnotify queue overflow") diff --git a/vendor/gopkg.in/fsnotify.v1/inotify.go b/vendor/gopkg.in/fsnotify.v1/inotify.go deleted file mode 100644 index d9fd1b88a0..0000000000 --- a/vendor/gopkg.in/fsnotify.v1/inotify.go +++ /dev/null @@ -1,337 +0,0 @@ -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build linux - -package fsnotify - -import ( - "errors" - "fmt" - "io" - "os" - "path/filepath" - "strings" - "sync" - "unsafe" - - "golang.org/x/sys/unix" -) - -// Watcher watches a set of files, delivering events to a channel. -type Watcher struct { - Events chan Event - Errors chan error - mu sync.Mutex // Map access - fd int - poller *fdPoller - watches map[string]*watch // Map of inotify watches (key: path) - paths map[int]string // Map of watched paths (key: watch descriptor) - done chan struct{} // Channel for sending a "quit message" to the reader goroutine - doneResp chan struct{} // Channel to respond to Close -} - -// NewWatcher establishes a new watcher with the underlying OS and begins waiting for events. -func NewWatcher() (*Watcher, error) { - // Create inotify fd - fd, errno := unix.InotifyInit1(unix.IN_CLOEXEC) - if fd == -1 { - return nil, errno - } - // Create epoll - poller, err := newFdPoller(fd) - if err != nil { - unix.Close(fd) - return nil, err - } - w := &Watcher{ - fd: fd, - poller: poller, - watches: make(map[string]*watch), - paths: make(map[int]string), - Events: make(chan Event), - Errors: make(chan error), - done: make(chan struct{}), - doneResp: make(chan struct{}), - } - - go w.readEvents() - return w, nil -} - -func (w *Watcher) isClosed() bool { - select { - case <-w.done: - return true - default: - return false - } -} - -// Close removes all watches and closes the events channel. -func (w *Watcher) Close() error { - if w.isClosed() { - return nil - } - - // Send 'close' signal to goroutine, and set the Watcher to closed. - close(w.done) - - // Wake up goroutine - w.poller.wake() - - // Wait for goroutine to close - <-w.doneResp - - return nil -} - -// Add starts watching the named file or directory (non-recursively). -func (w *Watcher) Add(name string) error { - name = filepath.Clean(name) - if w.isClosed() { - return errors.New("inotify instance already closed") - } - - const agnosticEvents = unix.IN_MOVED_TO | unix.IN_MOVED_FROM | - unix.IN_CREATE | unix.IN_ATTRIB | unix.IN_MODIFY | - unix.IN_MOVE_SELF | unix.IN_DELETE | unix.IN_DELETE_SELF - - var flags uint32 = agnosticEvents - - w.mu.Lock() - defer w.mu.Unlock() - watchEntry := w.watches[name] - if watchEntry != nil { - flags |= watchEntry.flags | unix.IN_MASK_ADD - } - wd, errno := unix.InotifyAddWatch(w.fd, name, flags) - if wd == -1 { - return errno - } - - if watchEntry == nil { - w.watches[name] = &watch{wd: uint32(wd), flags: flags} - w.paths[wd] = name - } else { - watchEntry.wd = uint32(wd) - watchEntry.flags = flags - } - - return nil -} - -// Remove stops watching the named file or directory (non-recursively). -func (w *Watcher) Remove(name string) error { - name = filepath.Clean(name) - - // Fetch the watch. - w.mu.Lock() - defer w.mu.Unlock() - watch, ok := w.watches[name] - - // Remove it from inotify. - if !ok { - return fmt.Errorf("can't remove non-existent inotify watch for: %s", name) - } - - // We successfully removed the watch if InotifyRmWatch doesn't return an - // error, we need to clean up our internal state to ensure it matches - // inotify's kernel state. - delete(w.paths, int(watch.wd)) - delete(w.watches, name) - - // inotify_rm_watch will return EINVAL if the file has been deleted; - // the inotify will already have been removed. - // watches and pathes are deleted in ignoreLinux() implicitly and asynchronously - // by calling inotify_rm_watch() below. e.g. readEvents() goroutine receives IN_IGNORE - // so that EINVAL means that the wd is being rm_watch()ed or its file removed - // by another thread and we have not received IN_IGNORE event. - success, errno := unix.InotifyRmWatch(w.fd, watch.wd) - if success == -1 { - // TODO: Perhaps it's not helpful to return an error here in every case. - // the only two possible errors are: - // EBADF, which happens when w.fd is not a valid file descriptor of any kind. - // EINVAL, which is when fd is not an inotify descriptor or wd is not a valid watch descriptor. - // Watch descriptors are invalidated when they are removed explicitly or implicitly; - // explicitly by inotify_rm_watch, implicitly when the file they are watching is deleted. - return errno - } - - return nil -} - -type watch struct { - wd uint32 // Watch descriptor (as returned by the inotify_add_watch() syscall) - flags uint32 // inotify flags of this watch (see inotify(7) for the list of valid flags) -} - -// readEvents reads from the inotify file descriptor, converts the -// received events into Event objects and sends them via the Events channel -func (w *Watcher) readEvents() { - var ( - buf [unix.SizeofInotifyEvent * 4096]byte // Buffer for a maximum of 4096 raw events - n int // Number of bytes read with read() - errno error // Syscall errno - ok bool // For poller.wait - ) - - defer close(w.doneResp) - defer close(w.Errors) - defer close(w.Events) - defer unix.Close(w.fd) - defer w.poller.close() - - for { - // See if we have been closed. - if w.isClosed() { - return - } - - ok, errno = w.poller.wait() - if errno != nil { - select { - case w.Errors <- errno: - case <-w.done: - return - } - continue - } - - if !ok { - continue - } - - n, errno = unix.Read(w.fd, buf[:]) - // If a signal interrupted execution, see if we've been asked to close, and try again. - // http://man7.org/linux/man-pages/man7/signal.7.html : - // "Before Linux 3.8, reads from an inotify(7) file descriptor were not restartable" - if errno == unix.EINTR { - continue - } - - // unix.Read might have been woken up by Close. If so, we're done. - if w.isClosed() { - return - } - - if n < unix.SizeofInotifyEvent { - var err error - if n == 0 { - // If EOF is received. This should really never happen. - err = io.EOF - } else if n < 0 { - // If an error occurred while reading. - err = errno - } else { - // Read was too short. - err = errors.New("notify: short read in readEvents()") - } - select { - case w.Errors <- err: - case <-w.done: - return - } - continue - } - - var offset uint32 - // We don't know how many events we just read into the buffer - // While the offset points to at least one whole event... - for offset <= uint32(n-unix.SizeofInotifyEvent) { - // Point "raw" to the event in the buffer - raw := (*unix.InotifyEvent)(unsafe.Pointer(&buf[offset])) - - mask := uint32(raw.Mask) - nameLen := uint32(raw.Len) - - if mask&unix.IN_Q_OVERFLOW != 0 { - select { - case w.Errors <- ErrEventOverflow: - case <-w.done: - return - } - } - - // If the event happened to the watched directory or the watched file, the kernel - // doesn't append the filename to the event, but we would like to always fill the - // the "Name" field with a valid filename. We retrieve the path of the watch from - // the "paths" map. - w.mu.Lock() - name, ok := w.paths[int(raw.Wd)] - // IN_DELETE_SELF occurs when the file/directory being watched is removed. - // This is a sign to clean up the maps, otherwise we are no longer in sync - // with the inotify kernel state which has already deleted the watch - // automatically. - if ok && mask&unix.IN_DELETE_SELF == unix.IN_DELETE_SELF { - delete(w.paths, int(raw.Wd)) - delete(w.watches, name) - } - w.mu.Unlock() - - if nameLen > 0 { - // Point "bytes" at the first byte of the filename - bytes := (*[unix.PathMax]byte)(unsafe.Pointer(&buf[offset+unix.SizeofInotifyEvent])) - // The filename is padded with NULL bytes. TrimRight() gets rid of those. - name += "/" + strings.TrimRight(string(bytes[0:nameLen]), "\000") - } - - event := newEvent(name, mask) - - // Send the events that are not ignored on the events channel - if !event.ignoreLinux(mask) { - select { - case w.Events <- event: - case <-w.done: - return - } - } - - // Move to the next event in the buffer - offset += unix.SizeofInotifyEvent + nameLen - } - } -} - -// Certain types of events can be "ignored" and not sent over the Events -// channel. Such as events marked ignore by the kernel, or MODIFY events -// against files that do not exist. -func (e *Event) ignoreLinux(mask uint32) bool { - // Ignore anything the inotify API says to ignore - if mask&unix.IN_IGNORED == unix.IN_IGNORED { - return true - } - - // If the event is not a DELETE or RENAME, the file must exist. - // Otherwise the event is ignored. - // *Note*: this was put in place because it was seen that a MODIFY - // event was sent after the DELETE. This ignores that MODIFY and - // assumes a DELETE will come or has come if the file doesn't exist. - if !(e.Op&Remove == Remove || e.Op&Rename == Rename) { - _, statErr := os.Lstat(e.Name) - return os.IsNotExist(statErr) - } - return false -} - -// newEvent returns an platform-independent Event based on an inotify mask. -func newEvent(name string, mask uint32) Event { - e := Event{Name: name} - if mask&unix.IN_CREATE == unix.IN_CREATE || mask&unix.IN_MOVED_TO == unix.IN_MOVED_TO { - e.Op |= Create - } - if mask&unix.IN_DELETE_SELF == unix.IN_DELETE_SELF || mask&unix.IN_DELETE == unix.IN_DELETE { - e.Op |= Remove - } - if mask&unix.IN_MODIFY == unix.IN_MODIFY { - e.Op |= Write - } - if mask&unix.IN_MOVE_SELF == unix.IN_MOVE_SELF || mask&unix.IN_MOVED_FROM == unix.IN_MOVED_FROM { - e.Op |= Rename - } - if mask&unix.IN_ATTRIB == unix.IN_ATTRIB { - e.Op |= Chmod - } - return e -} diff --git a/vendor/gopkg.in/fsnotify.v1/inotify_poller.go b/vendor/gopkg.in/fsnotify.v1/inotify_poller.go deleted file mode 100644 index cc7db4b22e..0000000000 --- a/vendor/gopkg.in/fsnotify.v1/inotify_poller.go +++ /dev/null @@ -1,187 +0,0 @@ -// Copyright 2015 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build linux - -package fsnotify - -import ( - "errors" - - "golang.org/x/sys/unix" -) - -type fdPoller struct { - fd int // File descriptor (as returned by the inotify_init() syscall) - epfd int // Epoll file descriptor - pipe [2]int // Pipe for waking up -} - -func emptyPoller(fd int) *fdPoller { - poller := new(fdPoller) - poller.fd = fd - poller.epfd = -1 - poller.pipe[0] = -1 - poller.pipe[1] = -1 - return poller -} - -// Create a new inotify poller. -// This creates an inotify handler, and an epoll handler. -func newFdPoller(fd int) (*fdPoller, error) { - var errno error - poller := emptyPoller(fd) - defer func() { - if errno != nil { - poller.close() - } - }() - poller.fd = fd - - // Create epoll fd - poller.epfd, errno = unix.EpollCreate1(0) - if poller.epfd == -1 { - return nil, errno - } - // Create pipe; pipe[0] is the read end, pipe[1] the write end. - errno = unix.Pipe2(poller.pipe[:], unix.O_NONBLOCK) - if errno != nil { - return nil, errno - } - - // Register inotify fd with epoll - event := unix.EpollEvent{ - Fd: int32(poller.fd), - Events: unix.EPOLLIN, - } - errno = unix.EpollCtl(poller.epfd, unix.EPOLL_CTL_ADD, poller.fd, &event) - if errno != nil { - return nil, errno - } - - // Register pipe fd with epoll - event = unix.EpollEvent{ - Fd: int32(poller.pipe[0]), - Events: unix.EPOLLIN, - } - errno = unix.EpollCtl(poller.epfd, unix.EPOLL_CTL_ADD, poller.pipe[0], &event) - if errno != nil { - return nil, errno - } - - return poller, nil -} - -// Wait using epoll. -// Returns true if something is ready to be read, -// false if there is not. -func (poller *fdPoller) wait() (bool, error) { - // 3 possible events per fd, and 2 fds, makes a maximum of 6 events. - // I don't know whether epoll_wait returns the number of events returned, - // or the total number of events ready. - // I decided to catch both by making the buffer one larger than the maximum. - events := make([]unix.EpollEvent, 7) - for { - n, errno := unix.EpollWait(poller.epfd, events, -1) - if n == -1 { - if errno == unix.EINTR { - continue - } - return false, errno - } - if n == 0 { - // If there are no events, try again. - continue - } - if n > 6 { - // This should never happen. More events were returned than should be possible. - return false, errors.New("epoll_wait returned more events than I know what to do with") - } - ready := events[:n] - epollhup := false - epollerr := false - epollin := false - for _, event := range ready { - if event.Fd == int32(poller.fd) { - if event.Events&unix.EPOLLHUP != 0 { - // This should not happen, but if it does, treat it as a wakeup. - epollhup = true - } - if event.Events&unix.EPOLLERR != 0 { - // If an error is waiting on the file descriptor, we should pretend - // something is ready to read, and let unix.Read pick up the error. - epollerr = true - } - if event.Events&unix.EPOLLIN != 0 { - // There is data to read. - epollin = true - } - } - if event.Fd == int32(poller.pipe[0]) { - if event.Events&unix.EPOLLHUP != 0 { - // Write pipe descriptor was closed, by us. This means we're closing down the - // watcher, and we should wake up. - } - if event.Events&unix.EPOLLERR != 0 { - // If an error is waiting on the pipe file descriptor. - // This is an absolute mystery, and should never ever happen. - return false, errors.New("Error on the pipe descriptor.") - } - if event.Events&unix.EPOLLIN != 0 { - // This is a regular wakeup, so we have to clear the buffer. - err := poller.clearWake() - if err != nil { - return false, err - } - } - } - } - - if epollhup || epollerr || epollin { - return true, nil - } - return false, nil - } -} - -// Close the write end of the poller. -func (poller *fdPoller) wake() error { - buf := make([]byte, 1) - n, errno := unix.Write(poller.pipe[1], buf) - if n == -1 { - if errno == unix.EAGAIN { - // Buffer is full, poller will wake. - return nil - } - return errno - } - return nil -} - -func (poller *fdPoller) clearWake() error { - // You have to be woken up a LOT in order to get to 100! - buf := make([]byte, 100) - n, errno := unix.Read(poller.pipe[0], buf) - if n == -1 { - if errno == unix.EAGAIN { - // Buffer is empty, someone else cleared our wake. - return nil - } - return errno - } - return nil -} - -// Close all poller file descriptors, but not the one passed to it. -func (poller *fdPoller) close() { - if poller.pipe[1] != -1 { - unix.Close(poller.pipe[1]) - } - if poller.pipe[0] != -1 { - unix.Close(poller.pipe[0]) - } - if poller.epfd != -1 { - unix.Close(poller.epfd) - } -} diff --git a/vendor/gopkg.in/fsnotify.v1/kqueue.go b/vendor/gopkg.in/fsnotify.v1/kqueue.go deleted file mode 100644 index 86e76a3d67..0000000000 --- a/vendor/gopkg.in/fsnotify.v1/kqueue.go +++ /dev/null @@ -1,521 +0,0 @@ -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build freebsd openbsd netbsd dragonfly darwin - -package fsnotify - -import ( - "errors" - "fmt" - "io/ioutil" - "os" - "path/filepath" - "sync" - "time" - - "golang.org/x/sys/unix" -) - -// Watcher watches a set of files, delivering events to a channel. -type Watcher struct { - Events chan Event - Errors chan error - done chan struct{} // Channel for sending a "quit message" to the reader goroutine - - kq int // File descriptor (as returned by the kqueue() syscall). - - mu sync.Mutex // Protects access to watcher data - watches map[string]int // Map of watched file descriptors (key: path). - externalWatches map[string]bool // Map of watches added by user of the library. - dirFlags map[string]uint32 // Map of watched directories to fflags used in kqueue. - paths map[int]pathInfo // Map file descriptors to path names for processing kqueue events. - fileExists map[string]bool // Keep track of if we know this file exists (to stop duplicate create events). - isClosed bool // Set to true when Close() is first called -} - -type pathInfo struct { - name string - isDir bool -} - -// NewWatcher establishes a new watcher with the underlying OS and begins waiting for events. -func NewWatcher() (*Watcher, error) { - kq, err := kqueue() - if err != nil { - return nil, err - } - - w := &Watcher{ - kq: kq, - watches: make(map[string]int), - dirFlags: make(map[string]uint32), - paths: make(map[int]pathInfo), - fileExists: make(map[string]bool), - externalWatches: make(map[string]bool), - Events: make(chan Event), - Errors: make(chan error), - done: make(chan struct{}), - } - - go w.readEvents() - return w, nil -} - -// Close removes all watches and closes the events channel. -func (w *Watcher) Close() error { - w.mu.Lock() - if w.isClosed { - w.mu.Unlock() - return nil - } - w.isClosed = true - - // copy paths to remove while locked - var pathsToRemove = make([]string, 0, len(w.watches)) - for name := range w.watches { - pathsToRemove = append(pathsToRemove, name) - } - w.mu.Unlock() - // unlock before calling Remove, which also locks - - for _, name := range pathsToRemove { - w.Remove(name) - } - - // send a "quit" message to the reader goroutine - close(w.done) - - return nil -} - -// Add starts watching the named file or directory (non-recursively). -func (w *Watcher) Add(name string) error { - w.mu.Lock() - w.externalWatches[name] = true - w.mu.Unlock() - _, err := w.addWatch(name, noteAllEvents) - return err -} - -// Remove stops watching the the named file or directory (non-recursively). -func (w *Watcher) Remove(name string) error { - name = filepath.Clean(name) - w.mu.Lock() - watchfd, ok := w.watches[name] - w.mu.Unlock() - if !ok { - return fmt.Errorf("can't remove non-existent kevent watch for: %s", name) - } - - const registerRemove = unix.EV_DELETE - if err := register(w.kq, []int{watchfd}, registerRemove, 0); err != nil { - return err - } - - unix.Close(watchfd) - - w.mu.Lock() - isDir := w.paths[watchfd].isDir - delete(w.watches, name) - delete(w.paths, watchfd) - delete(w.dirFlags, name) - w.mu.Unlock() - - // Find all watched paths that are in this directory that are not external. - if isDir { - var pathsToRemove []string - w.mu.Lock() - for _, path := range w.paths { - wdir, _ := filepath.Split(path.name) - if filepath.Clean(wdir) == name { - if !w.externalWatches[path.name] { - pathsToRemove = append(pathsToRemove, path.name) - } - } - } - w.mu.Unlock() - for _, name := range pathsToRemove { - // Since these are internal, not much sense in propagating error - // to the user, as that will just confuse them with an error about - // a path they did not explicitly watch themselves. - w.Remove(name) - } - } - - return nil -} - -// Watch all events (except NOTE_EXTEND, NOTE_LINK, NOTE_REVOKE) -const noteAllEvents = unix.NOTE_DELETE | unix.NOTE_WRITE | unix.NOTE_ATTRIB | unix.NOTE_RENAME - -// keventWaitTime to block on each read from kevent -var keventWaitTime = durationToTimespec(100 * time.Millisecond) - -// addWatch adds name to the watched file set. -// The flags are interpreted as described in kevent(2). -// Returns the real path to the file which was added, if any, which may be different from the one passed in the case of symlinks. -func (w *Watcher) addWatch(name string, flags uint32) (string, error) { - var isDir bool - // Make ./name and name equivalent - name = filepath.Clean(name) - - w.mu.Lock() - if w.isClosed { - w.mu.Unlock() - return "", errors.New("kevent instance already closed") - } - watchfd, alreadyWatching := w.watches[name] - // We already have a watch, but we can still override flags. - if alreadyWatching { - isDir = w.paths[watchfd].isDir - } - w.mu.Unlock() - - if !alreadyWatching { - fi, err := os.Lstat(name) - if err != nil { - return "", err - } - - // Don't watch sockets. - if fi.Mode()&os.ModeSocket == os.ModeSocket { - return "", nil - } - - // Don't watch named pipes. - if fi.Mode()&os.ModeNamedPipe == os.ModeNamedPipe { - return "", nil - } - - // Follow Symlinks - // Unfortunately, Linux can add bogus symlinks to watch list without - // issue, and Windows can't do symlinks period (AFAIK). To maintain - // consistency, we will act like everything is fine. There will simply - // be no file events for broken symlinks. - // Hence the returns of nil on errors. - if fi.Mode()&os.ModeSymlink == os.ModeSymlink { - name, err = filepath.EvalSymlinks(name) - if err != nil { - return "", nil - } - - w.mu.Lock() - _, alreadyWatching = w.watches[name] - w.mu.Unlock() - - if alreadyWatching { - return name, nil - } - - fi, err = os.Lstat(name) - if err != nil { - return "", nil - } - } - - watchfd, err = unix.Open(name, openMode, 0700) - if watchfd == -1 { - return "", err - } - - isDir = fi.IsDir() - } - - const registerAdd = unix.EV_ADD | unix.EV_CLEAR | unix.EV_ENABLE - if err := register(w.kq, []int{watchfd}, registerAdd, flags); err != nil { - unix.Close(watchfd) - return "", err - } - - if !alreadyWatching { - w.mu.Lock() - w.watches[name] = watchfd - w.paths[watchfd] = pathInfo{name: name, isDir: isDir} - w.mu.Unlock() - } - - if isDir { - // Watch the directory if it has not been watched before, - // or if it was watched before, but perhaps only a NOTE_DELETE (watchDirectoryFiles) - w.mu.Lock() - - watchDir := (flags&unix.NOTE_WRITE) == unix.NOTE_WRITE && - (!alreadyWatching || (w.dirFlags[name]&unix.NOTE_WRITE) != unix.NOTE_WRITE) - // Store flags so this watch can be updated later - w.dirFlags[name] = flags - w.mu.Unlock() - - if watchDir { - if err := w.watchDirectoryFiles(name); err != nil { - return "", err - } - } - } - return name, nil -} - -// readEvents reads from kqueue and converts the received kevents into -// Event values that it sends down the Events channel. -func (w *Watcher) readEvents() { - eventBuffer := make([]unix.Kevent_t, 10) - -loop: - for { - // See if there is a message on the "done" channel - select { - case <-w.done: - break loop - default: - } - - // Get new events - kevents, err := read(w.kq, eventBuffer, &keventWaitTime) - // EINTR is okay, the syscall was interrupted before timeout expired. - if err != nil && err != unix.EINTR { - select { - case w.Errors <- err: - case <-w.done: - break loop - } - continue - } - - // Flush the events we received to the Events channel - for len(kevents) > 0 { - kevent := &kevents[0] - watchfd := int(kevent.Ident) - mask := uint32(kevent.Fflags) - w.mu.Lock() - path := w.paths[watchfd] - w.mu.Unlock() - event := newEvent(path.name, mask) - - if path.isDir && !(event.Op&Remove == Remove) { - // Double check to make sure the directory exists. This can happen when - // we do a rm -fr on a recursively watched folders and we receive a - // modification event first but the folder has been deleted and later - // receive the delete event - if _, err := os.Lstat(event.Name); os.IsNotExist(err) { - // mark is as delete event - event.Op |= Remove - } - } - - if event.Op&Rename == Rename || event.Op&Remove == Remove { - w.Remove(event.Name) - w.mu.Lock() - delete(w.fileExists, event.Name) - w.mu.Unlock() - } - - if path.isDir && event.Op&Write == Write && !(event.Op&Remove == Remove) { - w.sendDirectoryChangeEvents(event.Name) - } else { - // Send the event on the Events channel. - select { - case w.Events <- event: - case <-w.done: - break loop - } - } - - if event.Op&Remove == Remove { - // Look for a file that may have overwritten this. - // For example, mv f1 f2 will delete f2, then create f2. - if path.isDir { - fileDir := filepath.Clean(event.Name) - w.mu.Lock() - _, found := w.watches[fileDir] - w.mu.Unlock() - if found { - // make sure the directory exists before we watch for changes. When we - // do a recursive watch and perform rm -fr, the parent directory might - // have gone missing, ignore the missing directory and let the - // upcoming delete event remove the watch from the parent directory. - if _, err := os.Lstat(fileDir); err == nil { - w.sendDirectoryChangeEvents(fileDir) - } - } - } else { - filePath := filepath.Clean(event.Name) - if fileInfo, err := os.Lstat(filePath); err == nil { - w.sendFileCreatedEventIfNew(filePath, fileInfo) - } - } - } - - // Move to next event - kevents = kevents[1:] - } - } - - // cleanup - err := unix.Close(w.kq) - if err != nil { - // only way the previous loop breaks is if w.done was closed so we need to async send to w.Errors. - select { - case w.Errors <- err: - default: - } - } - close(w.Events) - close(w.Errors) -} - -// newEvent returns an platform-independent Event based on kqueue Fflags. -func newEvent(name string, mask uint32) Event { - e := Event{Name: name} - if mask&unix.NOTE_DELETE == unix.NOTE_DELETE { - e.Op |= Remove - } - if mask&unix.NOTE_WRITE == unix.NOTE_WRITE { - e.Op |= Write - } - if mask&unix.NOTE_RENAME == unix.NOTE_RENAME { - e.Op |= Rename - } - if mask&unix.NOTE_ATTRIB == unix.NOTE_ATTRIB { - e.Op |= Chmod - } - return e -} - -func newCreateEvent(name string) Event { - return Event{Name: name, Op: Create} -} - -// watchDirectoryFiles to mimic inotify when adding a watch on a directory -func (w *Watcher) watchDirectoryFiles(dirPath string) error { - // Get all files - files, err := ioutil.ReadDir(dirPath) - if err != nil { - return err - } - - for _, fileInfo := range files { - filePath := filepath.Join(dirPath, fileInfo.Name()) - filePath, err = w.internalWatch(filePath, fileInfo) - if err != nil { - return err - } - - w.mu.Lock() - w.fileExists[filePath] = true - w.mu.Unlock() - } - - return nil -} - -// sendDirectoryEvents searches the directory for newly created files -// and sends them over the event channel. This functionality is to have -// the BSD version of fsnotify match Linux inotify which provides a -// create event for files created in a watched directory. -func (w *Watcher) sendDirectoryChangeEvents(dirPath string) { - // Get all files - files, err := ioutil.ReadDir(dirPath) - if err != nil { - select { - case w.Errors <- err: - case <-w.done: - return - } - } - - // Search for new files - for _, fileInfo := range files { - filePath := filepath.Join(dirPath, fileInfo.Name()) - err := w.sendFileCreatedEventIfNew(filePath, fileInfo) - - if err != nil { - return - } - } -} - -// sendFileCreatedEvent sends a create event if the file isn't already being tracked. -func (w *Watcher) sendFileCreatedEventIfNew(filePath string, fileInfo os.FileInfo) (err error) { - w.mu.Lock() - _, doesExist := w.fileExists[filePath] - w.mu.Unlock() - if !doesExist { - // Send create event - select { - case w.Events <- newCreateEvent(filePath): - case <-w.done: - return - } - } - - // like watchDirectoryFiles (but without doing another ReadDir) - filePath, err = w.internalWatch(filePath, fileInfo) - if err != nil { - return err - } - - w.mu.Lock() - w.fileExists[filePath] = true - w.mu.Unlock() - - return nil -} - -func (w *Watcher) internalWatch(name string, fileInfo os.FileInfo) (string, error) { - if fileInfo.IsDir() { - // mimic Linux providing delete events for subdirectories - // but preserve the flags used if currently watching subdirectory - w.mu.Lock() - flags := w.dirFlags[name] - w.mu.Unlock() - - flags |= unix.NOTE_DELETE | unix.NOTE_RENAME - return w.addWatch(name, flags) - } - - // watch file to mimic Linux inotify - return w.addWatch(name, noteAllEvents) -} - -// kqueue creates a new kernel event queue and returns a descriptor. -func kqueue() (kq int, err error) { - kq, err = unix.Kqueue() - if kq == -1 { - return kq, err - } - return kq, nil -} - -// register events with the queue -func register(kq int, fds []int, flags int, fflags uint32) error { - changes := make([]unix.Kevent_t, len(fds)) - - for i, fd := range fds { - // SetKevent converts int to the platform-specific types: - unix.SetKevent(&changes[i], fd, unix.EVFILT_VNODE, flags) - changes[i].Fflags = fflags - } - - // register the events - success, err := unix.Kevent(kq, changes, nil, nil) - if success == -1 { - return err - } - return nil -} - -// read retrieves pending events, or waits until an event occurs. -// A timeout of nil blocks indefinitely, while 0 polls the queue. -func read(kq int, events []unix.Kevent_t, timeout *unix.Timespec) ([]unix.Kevent_t, error) { - n, err := unix.Kevent(kq, nil, events, timeout) - if err != nil { - return nil, err - } - return events[0:n], nil -} - -// durationToTimespec prepares a timeout value -func durationToTimespec(d time.Duration) unix.Timespec { - return unix.NsecToTimespec(d.Nanoseconds()) -} diff --git a/vendor/gopkg.in/fsnotify.v1/open_mode_bsd.go b/vendor/gopkg.in/fsnotify.v1/open_mode_bsd.go deleted file mode 100644 index 7d8de14513..0000000000 --- a/vendor/gopkg.in/fsnotify.v1/open_mode_bsd.go +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright 2013 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build freebsd openbsd netbsd dragonfly - -package fsnotify - -import "golang.org/x/sys/unix" - -const openMode = unix.O_NONBLOCK | unix.O_RDONLY diff --git a/vendor/gopkg.in/fsnotify.v1/open_mode_darwin.go b/vendor/gopkg.in/fsnotify.v1/open_mode_darwin.go deleted file mode 100644 index 9139e17161..0000000000 --- a/vendor/gopkg.in/fsnotify.v1/open_mode_darwin.go +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright 2013 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build darwin - -package fsnotify - -import "golang.org/x/sys/unix" - -// note: this constant is not defined on BSD -const openMode = unix.O_EVTONLY diff --git a/vendor/gopkg.in/fsnotify.v1/windows.go b/vendor/gopkg.in/fsnotify.v1/windows.go deleted file mode 100644 index 09436f31d8..0000000000 --- a/vendor/gopkg.in/fsnotify.v1/windows.go +++ /dev/null @@ -1,561 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build windows - -package fsnotify - -import ( - "errors" - "fmt" - "os" - "path/filepath" - "runtime" - "sync" - "syscall" - "unsafe" -) - -// Watcher watches a set of files, delivering events to a channel. -type Watcher struct { - Events chan Event - Errors chan error - isClosed bool // Set to true when Close() is first called - mu sync.Mutex // Map access - port syscall.Handle // Handle to completion port - watches watchMap // Map of watches (key: i-number) - input chan *input // Inputs to the reader are sent on this channel - quit chan chan<- error -} - -// NewWatcher establishes a new watcher with the underlying OS and begins waiting for events. -func NewWatcher() (*Watcher, error) { - port, e := syscall.CreateIoCompletionPort(syscall.InvalidHandle, 0, 0, 0) - if e != nil { - return nil, os.NewSyscallError("CreateIoCompletionPort", e) - } - w := &Watcher{ - port: port, - watches: make(watchMap), - input: make(chan *input, 1), - Events: make(chan Event, 50), - Errors: make(chan error), - quit: make(chan chan<- error, 1), - } - go w.readEvents() - return w, nil -} - -// Close removes all watches and closes the events channel. -func (w *Watcher) Close() error { - if w.isClosed { - return nil - } - w.isClosed = true - - // Send "quit" message to the reader goroutine - ch := make(chan error) - w.quit <- ch - if err := w.wakeupReader(); err != nil { - return err - } - return <-ch -} - -// Add starts watching the named file or directory (non-recursively). -func (w *Watcher) Add(name string) error { - if w.isClosed { - return errors.New("watcher already closed") - } - in := &input{ - op: opAddWatch, - path: filepath.Clean(name), - flags: sysFSALLEVENTS, - reply: make(chan error), - } - w.input <- in - if err := w.wakeupReader(); err != nil { - return err - } - return <-in.reply -} - -// Remove stops watching the the named file or directory (non-recursively). -func (w *Watcher) Remove(name string) error { - in := &input{ - op: opRemoveWatch, - path: filepath.Clean(name), - reply: make(chan error), - } - w.input <- in - if err := w.wakeupReader(); err != nil { - return err - } - return <-in.reply -} - -const ( - // Options for AddWatch - sysFSONESHOT = 0x80000000 - sysFSONLYDIR = 0x1000000 - - // Events - sysFSACCESS = 0x1 - sysFSALLEVENTS = 0xfff - sysFSATTRIB = 0x4 - sysFSCLOSE = 0x18 - sysFSCREATE = 0x100 - sysFSDELETE = 0x200 - sysFSDELETESELF = 0x400 - sysFSMODIFY = 0x2 - sysFSMOVE = 0xc0 - sysFSMOVEDFROM = 0x40 - sysFSMOVEDTO = 0x80 - sysFSMOVESELF = 0x800 - - // Special events - sysFSIGNORED = 0x8000 - sysFSQOVERFLOW = 0x4000 -) - -func newEvent(name string, mask uint32) Event { - e := Event{Name: name} - if mask&sysFSCREATE == sysFSCREATE || mask&sysFSMOVEDTO == sysFSMOVEDTO { - e.Op |= Create - } - if mask&sysFSDELETE == sysFSDELETE || mask&sysFSDELETESELF == sysFSDELETESELF { - e.Op |= Remove - } - if mask&sysFSMODIFY == sysFSMODIFY { - e.Op |= Write - } - if mask&sysFSMOVE == sysFSMOVE || mask&sysFSMOVESELF == sysFSMOVESELF || mask&sysFSMOVEDFROM == sysFSMOVEDFROM { - e.Op |= Rename - } - if mask&sysFSATTRIB == sysFSATTRIB { - e.Op |= Chmod - } - return e -} - -const ( - opAddWatch = iota - opRemoveWatch -) - -const ( - provisional uint64 = 1 << (32 + iota) -) - -type input struct { - op int - path string - flags uint32 - reply chan error -} - -type inode struct { - handle syscall.Handle - volume uint32 - index uint64 -} - -type watch struct { - ov syscall.Overlapped - ino *inode // i-number - path string // Directory path - mask uint64 // Directory itself is being watched with these notify flags - names map[string]uint64 // Map of names being watched and their notify flags - rename string // Remembers the old name while renaming a file - buf [4096]byte -} - -type indexMap map[uint64]*watch -type watchMap map[uint32]indexMap - -func (w *Watcher) wakeupReader() error { - e := syscall.PostQueuedCompletionStatus(w.port, 0, 0, nil) - if e != nil { - return os.NewSyscallError("PostQueuedCompletionStatus", e) - } - return nil -} - -func getDir(pathname string) (dir string, err error) { - attr, e := syscall.GetFileAttributes(syscall.StringToUTF16Ptr(pathname)) - if e != nil { - return "", os.NewSyscallError("GetFileAttributes", e) - } - if attr&syscall.FILE_ATTRIBUTE_DIRECTORY != 0 { - dir = pathname - } else { - dir, _ = filepath.Split(pathname) - dir = filepath.Clean(dir) - } - return -} - -func getIno(path string) (ino *inode, err error) { - h, e := syscall.CreateFile(syscall.StringToUTF16Ptr(path), - syscall.FILE_LIST_DIRECTORY, - syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_DELETE, - nil, syscall.OPEN_EXISTING, - syscall.FILE_FLAG_BACKUP_SEMANTICS|syscall.FILE_FLAG_OVERLAPPED, 0) - if e != nil { - return nil, os.NewSyscallError("CreateFile", e) - } - var fi syscall.ByHandleFileInformation - if e = syscall.GetFileInformationByHandle(h, &fi); e != nil { - syscall.CloseHandle(h) - return nil, os.NewSyscallError("GetFileInformationByHandle", e) - } - ino = &inode{ - handle: h, - volume: fi.VolumeSerialNumber, - index: uint64(fi.FileIndexHigh)<<32 | uint64(fi.FileIndexLow), - } - return ino, nil -} - -// Must run within the I/O thread. -func (m watchMap) get(ino *inode) *watch { - if i := m[ino.volume]; i != nil { - return i[ino.index] - } - return nil -} - -// Must run within the I/O thread. -func (m watchMap) set(ino *inode, watch *watch) { - i := m[ino.volume] - if i == nil { - i = make(indexMap) - m[ino.volume] = i - } - i[ino.index] = watch -} - -// Must run within the I/O thread. -func (w *Watcher) addWatch(pathname string, flags uint64) error { - dir, err := getDir(pathname) - if err != nil { - return err - } - if flags&sysFSONLYDIR != 0 && pathname != dir { - return nil - } - ino, err := getIno(dir) - if err != nil { - return err - } - w.mu.Lock() - watchEntry := w.watches.get(ino) - w.mu.Unlock() - if watchEntry == nil { - if _, e := syscall.CreateIoCompletionPort(ino.handle, w.port, 0, 0); e != nil { - syscall.CloseHandle(ino.handle) - return os.NewSyscallError("CreateIoCompletionPort", e) - } - watchEntry = &watch{ - ino: ino, - path: dir, - names: make(map[string]uint64), - } - w.mu.Lock() - w.watches.set(ino, watchEntry) - w.mu.Unlock() - flags |= provisional - } else { - syscall.CloseHandle(ino.handle) - } - if pathname == dir { - watchEntry.mask |= flags - } else { - watchEntry.names[filepath.Base(pathname)] |= flags - } - if err = w.startRead(watchEntry); err != nil { - return err - } - if pathname == dir { - watchEntry.mask &= ^provisional - } else { - watchEntry.names[filepath.Base(pathname)] &= ^provisional - } - return nil -} - -// Must run within the I/O thread. -func (w *Watcher) remWatch(pathname string) error { - dir, err := getDir(pathname) - if err != nil { - return err - } - ino, err := getIno(dir) - if err != nil { - return err - } - w.mu.Lock() - watch := w.watches.get(ino) - w.mu.Unlock() - if watch == nil { - return fmt.Errorf("can't remove non-existent watch for: %s", pathname) - } - if pathname == dir { - w.sendEvent(watch.path, watch.mask&sysFSIGNORED) - watch.mask = 0 - } else { - name := filepath.Base(pathname) - w.sendEvent(filepath.Join(watch.path, name), watch.names[name]&sysFSIGNORED) - delete(watch.names, name) - } - return w.startRead(watch) -} - -// Must run within the I/O thread. -func (w *Watcher) deleteWatch(watch *watch) { - for name, mask := range watch.names { - if mask&provisional == 0 { - w.sendEvent(filepath.Join(watch.path, name), mask&sysFSIGNORED) - } - delete(watch.names, name) - } - if watch.mask != 0 { - if watch.mask&provisional == 0 { - w.sendEvent(watch.path, watch.mask&sysFSIGNORED) - } - watch.mask = 0 - } -} - -// Must run within the I/O thread. -func (w *Watcher) startRead(watch *watch) error { - if e := syscall.CancelIo(watch.ino.handle); e != nil { - w.Errors <- os.NewSyscallError("CancelIo", e) - w.deleteWatch(watch) - } - mask := toWindowsFlags(watch.mask) - for _, m := range watch.names { - mask |= toWindowsFlags(m) - } - if mask == 0 { - if e := syscall.CloseHandle(watch.ino.handle); e != nil { - w.Errors <- os.NewSyscallError("CloseHandle", e) - } - w.mu.Lock() - delete(w.watches[watch.ino.volume], watch.ino.index) - w.mu.Unlock() - return nil - } - e := syscall.ReadDirectoryChanges(watch.ino.handle, &watch.buf[0], - uint32(unsafe.Sizeof(watch.buf)), false, mask, nil, &watch.ov, 0) - if e != nil { - err := os.NewSyscallError("ReadDirectoryChanges", e) - if e == syscall.ERROR_ACCESS_DENIED && watch.mask&provisional == 0 { - // Watched directory was probably removed - if w.sendEvent(watch.path, watch.mask&sysFSDELETESELF) { - if watch.mask&sysFSONESHOT != 0 { - watch.mask = 0 - } - } - err = nil - } - w.deleteWatch(watch) - w.startRead(watch) - return err - } - return nil -} - -// readEvents reads from the I/O completion port, converts the -// received events into Event objects and sends them via the Events channel. -// Entry point to the I/O thread. -func (w *Watcher) readEvents() { - var ( - n, key uint32 - ov *syscall.Overlapped - ) - runtime.LockOSThread() - - for { - e := syscall.GetQueuedCompletionStatus(w.port, &n, &key, &ov, syscall.INFINITE) - watch := (*watch)(unsafe.Pointer(ov)) - - if watch == nil { - select { - case ch := <-w.quit: - w.mu.Lock() - var indexes []indexMap - for _, index := range w.watches { - indexes = append(indexes, index) - } - w.mu.Unlock() - for _, index := range indexes { - for _, watch := range index { - w.deleteWatch(watch) - w.startRead(watch) - } - } - var err error - if e := syscall.CloseHandle(w.port); e != nil { - err = os.NewSyscallError("CloseHandle", e) - } - close(w.Events) - close(w.Errors) - ch <- err - return - case in := <-w.input: - switch in.op { - case opAddWatch: - in.reply <- w.addWatch(in.path, uint64(in.flags)) - case opRemoveWatch: - in.reply <- w.remWatch(in.path) - } - default: - } - continue - } - - switch e { - case syscall.ERROR_MORE_DATA: - if watch == nil { - w.Errors <- errors.New("ERROR_MORE_DATA has unexpectedly null lpOverlapped buffer") - } else { - // The i/o succeeded but the buffer is full. - // In theory we should be building up a full packet. - // In practice we can get away with just carrying on. - n = uint32(unsafe.Sizeof(watch.buf)) - } - case syscall.ERROR_ACCESS_DENIED: - // Watched directory was probably removed - w.sendEvent(watch.path, watch.mask&sysFSDELETESELF) - w.deleteWatch(watch) - w.startRead(watch) - continue - case syscall.ERROR_OPERATION_ABORTED: - // CancelIo was called on this handle - continue - default: - w.Errors <- os.NewSyscallError("GetQueuedCompletionPort", e) - continue - case nil: - } - - var offset uint32 - for { - if n == 0 { - w.Events <- newEvent("", sysFSQOVERFLOW) - w.Errors <- errors.New("short read in readEvents()") - break - } - - // Point "raw" to the event in the buffer - raw := (*syscall.FileNotifyInformation)(unsafe.Pointer(&watch.buf[offset])) - buf := (*[syscall.MAX_PATH]uint16)(unsafe.Pointer(&raw.FileName)) - name := syscall.UTF16ToString(buf[:raw.FileNameLength/2]) - fullname := filepath.Join(watch.path, name) - - var mask uint64 - switch raw.Action { - case syscall.FILE_ACTION_REMOVED: - mask = sysFSDELETESELF - case syscall.FILE_ACTION_MODIFIED: - mask = sysFSMODIFY - case syscall.FILE_ACTION_RENAMED_OLD_NAME: - watch.rename = name - case syscall.FILE_ACTION_RENAMED_NEW_NAME: - if watch.names[watch.rename] != 0 { - watch.names[name] |= watch.names[watch.rename] - delete(watch.names, watch.rename) - mask = sysFSMOVESELF - } - } - - sendNameEvent := func() { - if w.sendEvent(fullname, watch.names[name]&mask) { - if watch.names[name]&sysFSONESHOT != 0 { - delete(watch.names, name) - } - } - } - if raw.Action != syscall.FILE_ACTION_RENAMED_NEW_NAME { - sendNameEvent() - } - if raw.Action == syscall.FILE_ACTION_REMOVED { - w.sendEvent(fullname, watch.names[name]&sysFSIGNORED) - delete(watch.names, name) - } - if w.sendEvent(fullname, watch.mask&toFSnotifyFlags(raw.Action)) { - if watch.mask&sysFSONESHOT != 0 { - watch.mask = 0 - } - } - if raw.Action == syscall.FILE_ACTION_RENAMED_NEW_NAME { - fullname = filepath.Join(watch.path, watch.rename) - sendNameEvent() - } - - // Move to the next event in the buffer - if raw.NextEntryOffset == 0 { - break - } - offset += raw.NextEntryOffset - - // Error! - if offset >= n { - w.Errors <- errors.New("Windows system assumed buffer larger than it is, events have likely been missed.") - break - } - } - - if err := w.startRead(watch); err != nil { - w.Errors <- err - } - } -} - -func (w *Watcher) sendEvent(name string, mask uint64) bool { - if mask == 0 { - return false - } - event := newEvent(name, uint32(mask)) - select { - case ch := <-w.quit: - w.quit <- ch - case w.Events <- event: - } - return true -} - -func toWindowsFlags(mask uint64) uint32 { - var m uint32 - if mask&sysFSACCESS != 0 { - m |= syscall.FILE_NOTIFY_CHANGE_LAST_ACCESS - } - if mask&sysFSMODIFY != 0 { - m |= syscall.FILE_NOTIFY_CHANGE_LAST_WRITE - } - if mask&sysFSATTRIB != 0 { - m |= syscall.FILE_NOTIFY_CHANGE_ATTRIBUTES - } - if mask&(sysFSMOVE|sysFSCREATE|sysFSDELETE) != 0 { - m |= syscall.FILE_NOTIFY_CHANGE_FILE_NAME | syscall.FILE_NOTIFY_CHANGE_DIR_NAME - } - return m -} - -func toFSnotifyFlags(action uint32) uint64 { - switch action { - case syscall.FILE_ACTION_ADDED: - return sysFSCREATE - case syscall.FILE_ACTION_REMOVED: - return sysFSDELETE - case syscall.FILE_ACTION_MODIFIED: - return sysFSMODIFY - case syscall.FILE_ACTION_RENAMED_OLD_NAME: - return sysFSMOVEDFROM - case syscall.FILE_ACTION_RENAMED_NEW_NAME: - return sysFSMOVEDTO - } - return 0 -} diff --git a/vendor/k8s.io/apiserver/LICENSE b/vendor/k8s.io/apiserver/LICENSE deleted file mode 100644 index d645695673..0000000000 --- a/vendor/k8s.io/apiserver/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/vendor/k8s.io/apiserver/pkg/authentication/user/doc.go b/vendor/k8s.io/apiserver/pkg/authentication/user/doc.go deleted file mode 100644 index 3d87fd72ca..0000000000 --- a/vendor/k8s.io/apiserver/pkg/authentication/user/doc.go +++ /dev/null @@ -1,19 +0,0 @@ -/* -Copyright 2014 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Package user contains utilities for dealing with simple user exchange in the auth -// packages. The user.Info interface defines an interface for exchanging that info. -package user // import "k8s.io/apiserver/pkg/authentication/user" diff --git a/vendor/k8s.io/apiserver/pkg/authentication/user/user.go b/vendor/k8s.io/apiserver/pkg/authentication/user/user.go deleted file mode 100644 index 1af6f2b277..0000000000 --- a/vendor/k8s.io/apiserver/pkg/authentication/user/user.go +++ /dev/null @@ -1,88 +0,0 @@ -/* -Copyright 2014 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package user - -// Info describes a user that has been authenticated to the system. -type Info interface { - // GetName returns the name that uniquely identifies this user among all - // other active users. - GetName() string - // GetUID returns a unique value for a particular user that will change - // if the user is removed from the system and another user is added with - // the same name. - GetUID() string - // GetGroups returns the names of the groups the user is a member of - GetGroups() []string - - // GetExtra can contain any additional information that the authenticator - // thought was interesting. One example would be scopes on a token. - // Keys in this map should be namespaced to the authenticator or - // authenticator/authorizer pair making use of them. - // For instance: "example.org/foo" instead of "foo" - // This is a map[string][]string because it needs to be serializeable into - // a SubjectAccessReviewSpec.authorization.k8s.io for proper authorization - // delegation flows - // In order to faithfully round-trip through an impersonation flow, these keys - // MUST be lowercase. - GetExtra() map[string][]string -} - -// DefaultInfo provides a simple user information exchange object -// for components that implement the UserInfo interface. -type DefaultInfo struct { - Name string - UID string - Groups []string - Extra map[string][]string -} - -func (i *DefaultInfo) GetName() string { - return i.Name -} - -func (i *DefaultInfo) GetUID() string { - return i.UID -} - -func (i *DefaultInfo) GetGroups() []string { - return i.Groups -} - -func (i *DefaultInfo) GetExtra() map[string][]string { - return i.Extra -} - -const ( - // well-known user and group names - SystemPrivilegedGroup = "system:masters" - NodesGroup = "system:nodes" - MonitoringGroup = "system:monitoring" - AllUnauthenticated = "system:unauthenticated" - AllAuthenticated = "system:authenticated" - - Anonymous = "system:anonymous" - APIServerUser = "system:apiserver" - - // core kubernetes process identities - KubeProxy = "system:kube-proxy" - KubeControllerManager = "system:kube-controller-manager" - KubeScheduler = "system:kube-scheduler" - - // CredentialIDKey is the key used in a user's "extra" to specify the unique - // identifier for this identity document). - CredentialIDKey = "authentication.kubernetes.io/credential-id" -) diff --git a/vendor/modules.txt b/vendor/modules.txt index ec77b357af..0f8bbbd136 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -246,7 +246,6 @@ github.com/openshift/library-go/pkg/apiserver/jsonpatch github.com/openshift/library-go/pkg/config/clusterstatus github.com/openshift/library-go/pkg/config/leaderelection github.com/openshift/library-go/pkg/controller/factory -github.com/openshift/library-go/pkg/crypto github.com/openshift/library-go/pkg/manifest github.com/openshift/library-go/pkg/operator/events github.com/openshift/library-go/pkg/operator/loglevel @@ -442,9 +441,6 @@ google.golang.org/protobuf/types/known/wrapperspb # gopkg.in/evanphx/json-patch.v4 v4.12.0 ## explicit gopkg.in/evanphx/json-patch.v4 -# gopkg.in/fsnotify.v1 v1.4.7 -## explicit -gopkg.in/fsnotify.v1 # gopkg.in/inf.v0 v0.9.1 ## explicit gopkg.in/inf.v0 @@ -578,9 +574,6 @@ k8s.io/apimachinery/pkg/version k8s.io/apimachinery/pkg/watch k8s.io/apimachinery/third_party/forked/golang/json k8s.io/apimachinery/third_party/forked/golang/reflect -# k8s.io/apiserver v0.32.1 -## explicit; go 1.23.0 -k8s.io/apiserver/pkg/authentication/user # k8s.io/client-go v0.32.1 ## explicit; go 1.23.0 k8s.io/client-go/applyconfigurations