diff --git a/bindata/bootkube/manifests/secret-loadbalancer-serving-signer.yaml b/bindata/bootkube/manifests/secret-loadbalancer-serving-signer.yaml new file mode 100644 index 0000000000..6f1f19cf6c --- /dev/null +++ b/bindata/bootkube/manifests/secret-loadbalancer-serving-signer.yaml @@ -0,0 +1,13 @@ +apiVersion: v1 +kind: Secret +metadata: + name: loadbalancer-serving-signer + namespace: openshift-kube-apiserver-operator + annotations: + "auth.openshift.io/certificate-not-before": {{ .Assets | load "kube-apiserver-lb-host-ip-signer.crt" | notBefore }} + "auth.openshift.io/certificate-not-after": {{ .Assets | load "kube-apiserver-lb-host-ip-signer.crt" | notAfter }} + "auth.openshift.io/certificate-issuer": {{ .Assets | load "kube-apiserver-lb-host-ip-signer.crt" | issuer }} +type: SecretTypeTLS +data: + tls.crt: {{ .Assets | load "kube-apiserver-lb-host-ip-signer.crt" | base64 }} + tls.key: {{ .Assets | load "kube-apiserver-lb-host-ip-signer.key" | base64 }} diff --git a/bindata/bootkube/manifests/secret-localhost-serving-signer.yaml b/bindata/bootkube/manifests/secret-localhost-serving-signer.yaml new file mode 100644 index 0000000000..4d474168bf --- /dev/null +++ b/bindata/bootkube/manifests/secret-localhost-serving-signer.yaml @@ -0,0 +1,13 @@ +apiVersion: v1 +kind: Secret +metadata: + name: localhost-serving-signer + namespace: openshift-kube-apiserver-operator + annotations: + "auth.openshift.io/certificate-not-before": {{ .Assets | load "kube-apiserver-localhost-signer.crt" | notBefore }} + "auth.openshift.io/certificate-not-after": {{ .Assets | load "kube-apiserver-localhost-signer.crt" | notAfter }} + "auth.openshift.io/certificate-issuer": {{ .Assets | load "kube-apiserver-localhost-signer.crt" | issuer }} +type: SecretTypeTLS +data: + tls.crt: {{ .Assets | load "kube-apiserver-localhost-signer.crt" | base64 }} + tls.key: {{ .Assets | load "kube-apiserver-localhost-signer.key" | base64 }} diff --git a/bindata/bootkube/manifests/secret-service-network-serving-signer.yaml b/bindata/bootkube/manifests/secret-service-network-serving-signer.yaml new file mode 100644 index 0000000000..d80f21504c --- /dev/null +++ b/bindata/bootkube/manifests/secret-service-network-serving-signer.yaml @@ -0,0 +1,13 @@ +apiVersion: v1 +kind: Secret +metadata: + name: service-network-serving-signer + namespace: openshift-kube-apiserver-operator + annotations: + "auth.openshift.io/certificate-not-before": {{ .Assets | load "kube-apiserver-service-network-signer.crt" | notBefore }} + "auth.openshift.io/certificate-not-after": {{ .Assets | load "kube-apiserver-service-network-signer.crt" | notAfter }} + "auth.openshift.io/certificate-issuer": {{ .Assets | load "kube-apiserver-service-network-signer.crt" | issuer }} +type: SecretTypeTLS +data: + tls.crt: {{ .Assets | load "kube-apiserver-service-network-signer.crt" | base64 }} + tls.key: {{ .Assets | load "kube-apiserver-service-network-signer.key" | base64 }} diff --git a/glide.lock b/glide.lock index 4d182138c1..756a2ac99d 100644 --- a/glide.lock +++ b/glide.lock @@ -1,5 +1,5 @@ hash: a25cc3f928d57d570b1fb82483d5db8fed46925d3a3696d7026eaa4404f269a6 -updated: 2019-02-21T11:02:51.736875033-05:00 +updated: 2019-02-21T16:11:08.300004163-05:00 imports: - name: bitbucket.org/ww/goautoneg version: 75cd24fc2f2c2a2088577d12123ddee5f54e0675 @@ -296,7 +296,7 @@ imports: - operator/informers/externalversions/operator/v1 - operator/listers/operator/v1 - name: github.com/openshift/library-go - version: 6467eab2ef6ec7ffdd67cd812e811cbbcd027c5c + version: 052c4104706cb9a67fc093cba11baf7980525109 subpackages: - pkg/assets - pkg/config/client @@ -427,7 +427,7 @@ imports: subpackages: - rate - name: golang.org/x/tools - version: a754db16a40a9d94359b6600b825419c0ca6f986 + version: 83362c3779f5f48611068d488a03ea7bbaddc81e subpackages: - container/intsets - name: google.golang.org/appengine diff --git a/pkg/operator/certrotationcontroller/certrotationcontroller.go b/pkg/operator/certrotationcontroller/certrotationcontroller.go index b29e2e6a0f..ee8d4a7407 100644 --- a/pkg/operator/certrotationcontroller/certrotationcontroller.go +++ b/pkg/operator/certrotationcontroller/certrotationcontroller.go @@ -3,14 +3,15 @@ package certrotationcontroller import ( "time" - "github.com/openshift/library-go/pkg/operator/events" - "github.com/openshift/library-go/pkg/operator/v1helpers" + "github.com/golang/glog" + "k8s.io/apiserver/pkg/authentication/user" "k8s.io/client-go/kubernetes" "github.com/openshift/cluster-kube-apiserver-operator/pkg/operator/operatorclient" "github.com/openshift/library-go/pkg/operator/certrotation" - "k8s.io/apiserver/pkg/authentication/user" + "github.com/openshift/library-go/pkg/operator/events" + "github.com/openshift/library-go/pkg/operator/v1helpers" ) type CertRotationController struct { @@ -65,6 +66,128 @@ func NewCertRotationController( } ret.certRotators = append(ret.certRotators, certRotator) + certRotator, err = certrotation.NewCertRotationController( + "LocalhostServing", + certrotation.SigningRotation{ + Namespace: operatorclient.OperatorNamespace, + Name: "localhost-serving-signer", + Validity: 10 * 365 * 24 * time.Hour, + RefreshPercentage: 0.5, + Informer: kubeInformersForNamespaces.InformersFor(operatorclient.OperatorNamespace).Core().V1().Secrets(), + Lister: kubeInformersForNamespaces.InformersFor(operatorclient.OperatorNamespace).Core().V1().Secrets().Lister(), + Client: kubeClient.CoreV1(), + EventRecorder: eventRecorder, + }, + certrotation.CABundleRotation{ + Namespace: operatorclient.OperatorNamespace, + Name: "localhost-serving-ca", + Informer: kubeInformersForNamespaces.InformersFor(operatorclient.OperatorNamespace).Core().V1().ConfigMaps(), + Lister: kubeInformersForNamespaces.InformersFor(operatorclient.OperatorNamespace).Core().V1().ConfigMaps().Lister(), + Client: kubeClient.CoreV1(), + EventRecorder: eventRecorder, + }, + certrotation.TargetRotation{ + Namespace: operatorclient.TargetNamespace, + Name: "localhost-serving-cert-certkey", + Validity: 1 * 4 * time.Hour, + RefreshPercentage: 0.5, + ServingRotation: &certrotation.ServingRotation{ + Hostnames: []string{"localhost", "127.0.0.1"}, + }, + Informer: kubeInformersForNamespaces.InformersFor(operatorclient.TargetNamespace).Core().V1().Secrets(), + Lister: kubeInformersForNamespaces.InformersFor(operatorclient.TargetNamespace).Core().V1().Secrets().Lister(), + Client: kubeClient.CoreV1(), + EventRecorder: eventRecorder, + }, + operatorClient, + ) + if err != nil { + return nil, err + } + ret.certRotators = append(ret.certRotators, certRotator) + + certRotator, err = certrotation.NewCertRotationController( + "ServiceNetworkServing", + certrotation.SigningRotation{ + Namespace: operatorclient.OperatorNamespace, + Name: "service-network-serving-signer", + Validity: 10 * 365 * 24 * time.Hour, + RefreshPercentage: 0.5, + Informer: kubeInformersForNamespaces.InformersFor(operatorclient.OperatorNamespace).Core().V1().Secrets(), + Lister: kubeInformersForNamespaces.InformersFor(operatorclient.OperatorNamespace).Core().V1().Secrets().Lister(), + Client: kubeClient.CoreV1(), + EventRecorder: eventRecorder, + }, + certrotation.CABundleRotation{ + Namespace: operatorclient.OperatorNamespace, + Name: "service-network-serving-ca", + Informer: kubeInformersForNamespaces.InformersFor(operatorclient.OperatorNamespace).Core().V1().ConfigMaps(), + Lister: kubeInformersForNamespaces.InformersFor(operatorclient.OperatorNamespace).Core().V1().ConfigMaps().Lister(), + Client: kubeClient.CoreV1(), + EventRecorder: eventRecorder, + }, + certrotation.TargetRotation{ + Namespace: operatorclient.TargetNamespace, + Name: "service-network-serving-certkey", + Validity: 1 * 4 * time.Hour, + RefreshPercentage: 0.5, + ServingRotation: &certrotation.ServingRotation{ + // TODO, this needs to be late binding so we can keep it up to date as network configuration changes + Hostnames: []string{"kubernetes", "kubernetes.default", "kubernetes.default.svc", "kubernetes.default.svc.cluster.local"}, + }, + Informer: kubeInformersForNamespaces.InformersFor(operatorclient.TargetNamespace).Core().V1().Secrets(), + Lister: kubeInformersForNamespaces.InformersFor(operatorclient.TargetNamespace).Core().V1().Secrets().Lister(), + Client: kubeClient.CoreV1(), + EventRecorder: eventRecorder, + }, + operatorClient, + ) + if err != nil { + return nil, err + } + ret.certRotators = append(ret.certRotators, certRotator) + + certRotator, err = certrotation.NewCertRotationController( + "LoadBalancerServing", + certrotation.SigningRotation{ + Namespace: operatorclient.OperatorNamespace, + Name: "loadbalancer-serving-signer", + Validity: 10 * 365 * 24 * time.Hour, + RefreshPercentage: 0.5, + Informer: kubeInformersForNamespaces.InformersFor(operatorclient.OperatorNamespace).Core().V1().Secrets(), + Lister: kubeInformersForNamespaces.InformersFor(operatorclient.OperatorNamespace).Core().V1().Secrets().Lister(), + Client: kubeClient.CoreV1(), + EventRecorder: eventRecorder, + }, + certrotation.CABundleRotation{ + Namespace: operatorclient.OperatorNamespace, + Name: "loadbalancer-serving-ca", + Informer: kubeInformersForNamespaces.InformersFor(operatorclient.OperatorNamespace).Core().V1().ConfigMaps(), + Lister: kubeInformersForNamespaces.InformersFor(operatorclient.OperatorNamespace).Core().V1().ConfigMaps().Lister(), + Client: kubeClient.CoreV1(), + EventRecorder: eventRecorder, + }, + certrotation.TargetRotation{ + Namespace: operatorclient.TargetNamespace, + Name: "loadbalancer-serving-serving-certkey", + Validity: 1 * 4 * time.Hour, + RefreshPercentage: 0.5, + ServingRotation: &certrotation.ServingRotation{ + // TODO, this needs to be late binding so we can keep it up to date as network configuration changes + Hostnames: []string{"api"}, // this isn't correct, but we need something to get certs started + }, + Informer: kubeInformersForNamespaces.InformersFor(operatorclient.TargetNamespace).Core().V1().Secrets(), + Lister: kubeInformersForNamespaces.InformersFor(operatorclient.TargetNamespace).Core().V1().Secrets().Lister(), + Client: kubeClient.CoreV1(), + EventRecorder: eventRecorder, + }, + operatorClient, + ) + if err != nil { + return nil, err + } + ret.certRotators = append(ret.certRotators, certRotator) + certRotator, err = certrotation.NewCertRotationController( "KubeControllerManagerClient", certrotation.SigningRotation{ @@ -191,6 +314,9 @@ func NewCertRotationController( } func (c *CertRotationController) Run(workers int, stopCh <-chan struct{}) { + glog.Infof("Starting CertRotation") + defer glog.Infof("Shutting down CertRotation") + for _, certRotator := range c.certRotators { go certRotator.Run(workers, stopCh) } diff --git a/vendor/github.com/openshift/library-go/pkg/assets/template.go b/vendor/github.com/openshift/library-go/pkg/assets/template.go index a145868ec9..7854392203 100644 --- a/vendor/github.com/openshift/library-go/pkg/assets/template.go +++ b/vendor/github.com/openshift/library-go/pkg/assets/template.go @@ -29,6 +29,9 @@ func base64encode(v []byte) string { } func notAfter(certBytes []byte) string { + if len(certBytes) == 0 { + return "" + } certs, err := cert.ParseCertsPEM(certBytes) if err != nil { panic(err) @@ -37,6 +40,9 @@ func notAfter(certBytes []byte) string { } func notBefore(certBytes []byte) string { + if len(certBytes) == 0 { + return "" + } certs, err := cert.ParseCertsPEM(certBytes) if err != nil { panic(err) @@ -45,6 +51,9 @@ func notBefore(certBytes []byte) string { } func issuer(certBytes []byte) string { + if len(certBytes) == 0 { + return "" + } certs, err := cert.ParseCertsPEM(certBytes) if err != nil { panic(err) diff --git a/vendor/golang.org/x/tools/go/gcexportdata/example_test.go b/vendor/golang.org/x/tools/go/gcexportdata/example_test.go index 3a205abf1d..c2a18561f6 100644 --- a/vendor/golang.org/x/tools/go/gcexportdata/example_test.go +++ b/vendor/golang.org/x/tools/go/gcexportdata/example_test.go @@ -70,15 +70,15 @@ func ExampleRead() { // ExampleNewImporter demonstrates usage of NewImporter to provide type // information for dependencies when type-checking Go source code. func ExampleNewImporter() { - const src = `package myscanner + const src = `package myrpc -// choosing a package that is unlikely to change across releases -import "text/scanner" +// choosing a package that doesn't change across releases +import "net/rpc" -const scanIdents = scanner.ScanIdents +const defaultRPCPath = rpc.DefaultRPCPath ` fset := token.NewFileSet() - f, err := parser.ParseFile(fset, "myscanner.go", src, 0) + f, err := parser.ParseFile(fset, "myrpc.go", src, 0) if err != nil { log.Fatal(err) } @@ -86,13 +86,13 @@ const scanIdents = scanner.ScanIdents packages := make(map[string]*types.Package) imp := gcexportdata.NewImporter(fset, packages) conf := types.Config{Importer: imp} - pkg, err := conf.Check("myscanner", fset, []*ast.File{f}, nil) + pkg, err := conf.Check("myrpc", fset, []*ast.File{f}, nil) if err != nil { log.Fatal(err) } // object from imported package - pi := packages["text/scanner"].Scope().Lookup("ScanIdents") + pi := packages["net/rpc"].Scope().Lookup("DefaultRPCPath") fmt.Printf("const %s.%s %s = %s // %s\n", pi.Pkg().Path(), pi.Name(), @@ -102,7 +102,7 @@ const scanIdents = scanner.ScanIdents ) // object in source package - twopi := pkg.Scope().Lookup("scanIdents") + twopi := pkg.Scope().Lookup("defaultRPCPath") fmt.Printf("const %s %s = %s // %s\n", twopi.Name(), twopi.Type(), @@ -112,8 +112,8 @@ const scanIdents = scanner.ScanIdents // Output: // - // const text/scanner.ScanIdents untyped int = 4 // $GOROOT/src/text/scanner/scanner.go:62:1 - // const scanIdents untyped int = 4 // myscanner.go:6:7 + // const net/rpc.DefaultRPCPath untyped string = "/_goRPC_" // $GOROOT/src/net/rpc/server.go:146:1 + // const defaultRPCPath untyped string = "/_goRPC_" // myrpc.go:6:7 } func slashify(posn token.Position) token.Position { diff --git a/vendor/golang.org/x/tools/internal/lsp/cache/view.go b/vendor/golang.org/x/tools/internal/lsp/cache/view.go index 01b533c00c..8c719eaddc 100644 --- a/vendor/golang.org/x/tools/internal/lsp/cache/view.go +++ b/vendor/golang.org/x/tools/internal/lsp/cache/view.go @@ -197,6 +197,9 @@ type entry struct { } func (imp *importer) addImports(path string, pkg *packages.Package) error { + if _, ok := imp.packages[path]; ok { + return nil + } imp.packages[path] = pkg for importPath, importPkg := range pkg.Imports { if err := imp.addImports(importPath, importPkg); err != nil { diff --git a/vendor/golang.org/x/tools/internal/lsp/lsp_test.go b/vendor/golang.org/x/tools/internal/lsp/lsp_test.go index 75d2292575..eea102f666 100644 --- a/vendor/golang.org/x/tools/internal/lsp/lsp_test.go +++ b/vendor/golang.org/x/tools/internal/lsp/lsp_test.go @@ -443,3 +443,31 @@ func (d definitions) collect(fset *token.FileSet, src, target packagestest.Range loc := toProtocolLocation(fset, source.Range(src)) d[loc] = toProtocolLocation(fset, source.Range(target)) } + +func TestBytesOffset(t *testing.T) { + tests := []struct { + text string + pos protocol.Position + want int + }{ + {text: `a𐐀b`, pos: protocol.Position{Line: 0, Character: 0}, want: 0}, + {text: `a𐐀b`, pos: protocol.Position{Line: 0, Character: 1}, want: 1}, + {text: `a𐐀b`, pos: protocol.Position{Line: 0, Character: 2}, want: 1}, + {text: `a𐐀b`, pos: protocol.Position{Line: 0, Character: 3}, want: 5}, + {text: `a𐐀b`, pos: protocol.Position{Line: 0, Character: 4}, want: -1}, + {text: "aaa\nbbb\n", pos: protocol.Position{Line: 0, Character: 3}, want: 3}, + {text: "aaa\nbbb\n", pos: protocol.Position{Line: 0, Character: 4}, want: -1}, + {text: "aaa\nbbb\n", pos: protocol.Position{Line: 1, Character: 0}, want: 4}, + {text: "aaa\nbbb\n", pos: protocol.Position{Line: 1, Character: 3}, want: 7}, + {text: "aaa\nbbb\n", pos: protocol.Position{Line: 1, Character: 4}, want: -1}, + {text: "aaa\nbbb\n", pos: protocol.Position{Line: 2, Character: 0}, want: -1}, + {text: "aaa\nbbb\n\n", pos: protocol.Position{Line: 2, Character: 0}, want: 8}, + } + + for _, test := range tests { + got := bytesOffset([]byte(test.text), test.pos) + if got != test.want { + t.Errorf("want %d for %q(Line:%d,Character:%d), but got %d", test.want, test.text, int(test.pos.Line), int(test.pos.Character), got) + } + } +} diff --git a/vendor/golang.org/x/tools/internal/lsp/protocol/text.go b/vendor/golang.org/x/tools/internal/lsp/protocol/text.go index 83456a415b..5d50b2a80d 100644 --- a/vendor/golang.org/x/tools/internal/lsp/protocol/text.go +++ b/vendor/golang.org/x/tools/internal/lsp/protocol/text.go @@ -38,7 +38,7 @@ type TextDocumentContentChangeEvent struct { /** * The range of the document that changed. */ - Range Range `json:"range,omitempty"` + Range *Range `json:"range,omitempty"` /** * The length of the range that got replaced. diff --git a/vendor/golang.org/x/tools/internal/lsp/server.go b/vendor/golang.org/x/tools/internal/lsp/server.go index 3db22fff13..121b721608 100644 --- a/vendor/golang.org/x/tools/internal/lsp/server.go +++ b/vendor/golang.org/x/tools/internal/lsp/server.go @@ -5,6 +5,7 @@ package lsp import ( + "bytes" "context" "fmt" "go/ast" @@ -13,6 +14,7 @@ import ( "net" "os" "sync" + "unicode/utf8" "golang.org/x/tools/go/packages" "golang.org/x/tools/internal/jsonrpc2" @@ -122,7 +124,7 @@ func (s *server) Initialize(ctx context.Context, params *protocol.InitializePara TriggerCharacters: []string{"(", ","}, }, TextDocumentSync: protocol.TextDocumentSyncOptions{ - Change: float64(protocol.Full), // full contents of file sent on each update + Change: float64(protocol.Incremental), OpenClose: true, }, TypeDefinitionProvider: true, @@ -177,16 +179,82 @@ func (s *server) DidOpen(ctx context.Context, params *protocol.DidOpenTextDocume return nil } +func bytesOffset(content []byte, pos protocol.Position) int { + var line, char, offset int + + for len(content) > 0 { + if line == int(pos.Line) && char == int(pos.Character) { + return offset + } + r, size := utf8.DecodeRune(content) + char++ + // The offsets are based on a UTF-16 string representation. + // So the rune should be checked twice for two code units in UTF-16. + if r >= 0x10000 { + if line == int(pos.Line) && char == int(pos.Character) { + return offset + } + char++ + } + offset += size + content = content[size:] + if r == '\n' { + line++ + char = 0 + } + } + return -1 +} + +func (s *server) applyChanges(ctx context.Context, params *protocol.DidChangeTextDocumentParams) (string, error) { + if len(params.ContentChanges) == 1 && params.ContentChanges[0].Range == nil { + // If range is empty, we expect the full content of file, i.e. a single change with no range. + change := params.ContentChanges[0] + if change.RangeLength != 0 { + return "", jsonrpc2.NewErrorf(jsonrpc2.CodeInternalError, "unexpected change range provided") + } + return change.Text, nil + } + + sourceURI, err := fromProtocolURI(params.TextDocument.URI) + if err != nil { + return "", err + } + + file, err := s.view.GetFile(ctx, sourceURI) + if err != nil { + return "", jsonrpc2.NewErrorf(jsonrpc2.CodeInternalError, "file not found") + } + + content := file.GetContent() + for _, change := range params.ContentChanges { + start := bytesOffset(content, change.Range.Start) + if start == -1 { + return "", jsonrpc2.NewErrorf(jsonrpc2.CodeInternalError, "invalid range for content change") + } + end := bytesOffset(content, change.Range.End) + if end == -1 { + return "", jsonrpc2.NewErrorf(jsonrpc2.CodeInternalError, "invalid range for content change") + } + var buf bytes.Buffer + buf.Write(content[:start]) + buf.WriteString(change.Text) + buf.Write(content[end:]) + content = buf.Bytes() + } + return string(content), nil +} + func (s *server) DidChange(ctx context.Context, params *protocol.DidChangeTextDocumentParams) error { if len(params.ContentChanges) < 1 { return jsonrpc2.NewErrorf(jsonrpc2.CodeInternalError, "no content changes provided") } - // We expect the full content of file, i.e. a single change with no range. - change := params.ContentChanges[0] - if change.RangeLength != 0 { - return jsonrpc2.NewErrorf(jsonrpc2.CodeInternalError, "unexpected change range provided") + + text, err := s.applyChanges(ctx, params) + if err != nil { + return err } - s.cacheAndDiagnose(ctx, params.TextDocument.URI, change.Text) + s.cacheAndDiagnose(ctx, params.TextDocument.URI, text) return nil }