From f51c2dbc42628d73ea2fc0aed52941fc78515f08 Mon Sep 17 00:00:00 2001 From: Seebs Date: Mon, 2 Dec 2019 13:24:53 -0600 Subject: [PATCH] use extensions through build tags --- Makefile | 1 + ext/ext.go | 25 +++++++++++++ ext/extensions/distinct.go | 21 +++++++++++ ext/extensions/dummy.go | 18 +++++++++ server.go | 75 +++++++------------------------------- 5 files changed, 78 insertions(+), 62 deletions(-) create mode 100644 ext/extensions/distinct.go create mode 100644 ext/extensions/dummy.go diff --git a/Makefile b/Makefile index bee802120..ac204d3ca 100644 --- a/Makefile +++ b/Makefile @@ -16,6 +16,7 @@ RELEASE_ENABLED = $(subst 0,,$(RELEASE)) BUILD_TAGS += $(if $(ENTERPRISE_ENABLED),enterprise) BUILD_TAGS += $(if $(RELEASE_ENABLED),release) BUILD_TAGS += shardwidth$(SHARD_WIDTH) +BUILD_TAGS += $(foreach p,$(PLUGINS),plugin$(p)) define LICENSE_HASH_CODE head -13 $1 | sed -e 's/Copyright 20[0-9][0-9]/Copyright 20XX/g' | shasum | cut -f 1 -d " " endef diff --git a/ext/ext.go b/ext/ext.go index b6daa3ef0..762ae44f8 100644 --- a/ext/ext.go +++ b/ext/ext.go @@ -38,6 +38,8 @@ // case, no ops are registered. package ext +import "sync" + // The Bitmap type represents a Pilosa bitmap, and is used for bitmap // operations. type Bitmap interface { @@ -236,3 +238,26 @@ type ExtensionInfo struct { License string // License info. BitmapOps []BitmapOp // List of provided ops. } + +var extMu sync.Mutex + +var knownExtensions []*ExtensionInfo +var newExtensions []*ExtensionInfo + +// RegisterExtension tells the extension system about a new extension. +func RegisterExtension(ext *ExtensionInfo) { + extMu.Lock() + defer extMu.Unlock() + newExtensions = append(newExtensions, ext) +} + +// NewExtentsions returns extensions that have been registered, but not previously +// returned by Newextensions. +func NewExtensions() []*ExtensionInfo { + extMu.Lock() + defer extMu.Unlock() + knownExtensions = append(knownExtensions, newExtensions...) + ret := newExtensions + newExtensions = nil + return ret +} diff --git a/ext/extensions/distinct.go b/ext/extensions/distinct.go new file mode 100644 index 000000000..42f11c90f --- /dev/null +++ b/ext/extensions/distinct.go @@ -0,0 +1,21 @@ +// Copyright 2019 Pilosa Corp. +// +// 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. + +// +build plugindistinct + +package extensions + +import ( + _ "github.com/molecula/extensions/distinct" +) diff --git a/ext/extensions/dummy.go b/ext/extensions/dummy.go new file mode 100644 index 000000000..cf418edb5 --- /dev/null +++ b/ext/extensions/dummy.go @@ -0,0 +1,18 @@ +// Copyright 2019 Pilosa Corp. +// +// 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. + +// This package contains only things which are conditional on build +// tags. + +package extensions diff --git a/server.go b/server.go index ddf43ed50..645418898 100644 --- a/server.go +++ b/server.go @@ -17,12 +17,10 @@ package pilosa import ( "context" "fmt" - "io" "log" "os" "os/exec" "path/filepath" - "plugin" "runtime" "strconv" "strings" @@ -30,6 +28,8 @@ import ( "time" "github.com/pilosa/pilosa/v2/ext" + // extensions pulls in some extensions depending on build tags + _ "github.com/pilosa/pilosa/v2/ext/extensions" "github.com/pilosa/pilosa/v2/logger" "github.com/pilosa/pilosa/v2/pql" "github.com/pilosa/pilosa/v2/roaring" @@ -341,8 +341,6 @@ func NewServer(opts ...ServerOption) (*Server, error) { if err != nil { return nil, err } - s.extensionPath = filepath.Join(path, ".extensions") - s.holder.Path = path // s.holder.translateFile.Path = filepath.Join(path, ".keys") s.holder.Logger = s.logger @@ -383,7 +381,7 @@ func NewServer(opts ...ServerOption) (*Server, error) { s.cluster.broadcaster = s s.cluster.maxWritesPerRequest = s.maxWritesPerRequest s.holder.broadcaster = s - err = s.loadPlugins() + err = s.loadExtensions() if err != nil { s.logger.Printf("not all plugins loaded successfully") } @@ -420,67 +418,20 @@ func (s *Server) InternalClient() InternalClient { return s.defaultClient } -func (s *Server) loadPlugins() error { - var anyError error - dir, err := os.Open(s.extensionPath) - if err != nil { - // don't complain about it not existing, that's fine. - if os.IsNotExist(err) { - s.logger.Printf("extension interface v0: no extensions directory.") - return nil - } - return errors.Wrap(err, "opening extension path:") - } - defer dir.Close() - for files, err := dir.Readdir(64); err != io.EOF; files, err = dir.Readdir(64) { - if err != nil { - return errors.Wrap(err, "searching extension directory:") - } - for _, file := range files { - name := file.Name() - // only .so files are likely plugins. - if !strings.HasSuffix(name, ".so") { - continue - } - // only regular files are candidates for loading. - mode := file.Mode() - if !mode.IsRegular() { - s.logger.Printf("extension file '%s' is not a regular file", name) - continue - } - err = s.loadPlugin(name) - if err != nil { - s.logger.Printf("loading extension %s: %v", name, err) - anyError = err - } +func (s *Server) loadExtensions() error { + exts := ext.NewExtensions() + var lastError error + for _, extension := range exts { + if err := s.loadExtension(extension); err != nil { + lastError = err } } - return anyError + return lastError } -func (s *Server) loadPlugin(name string) error { - path := filepath.Join(s.extensionPath, name) - p, err := plugin.Open(path) - if err != nil { - return err - } - pluginExtInfo, err := p.Lookup("ExtensionInfo") - if err != nil { - return fmt.Errorf("%s: no ExtensionInfo found", name) - } - extInfoFunc, ok := pluginExtInfo.(func(string) (*ext.ExtensionInfo, error)) - if !ok { - return fmt.Errorf("%s: unexpected %T instead of ExtensionInfo object", name, pluginExtInfo) - } - extInfo, err := extInfoFunc("v0") - if err != nil { - return errors.Wrap(err, name) - } - if extInfo == nil { - return fmt.Errorf("%s: nil ExtensionInfo", name) - } +func (s *Server) loadExtension(extInfo *ext.ExtensionInfo) error { if extInfo.ExtensionAPI != "v0" { - return fmt.Errorf("%s: unsupported extension API %s", name, extInfo.ExtensionAPI) + return fmt.Errorf("%s: unsupported extension API %s", extInfo.Name, extInfo.ExtensionAPI) } s.extensions = append(s.extensions, extInfo) bitmapOps := extInfo.BitmapOps @@ -500,7 +451,7 @@ func (s *Server) loadPlugin(name string) error { unknownOps++ } } - err = s.executor.registerOps(bitmapOps) + err := s.executor.registerOps(bitmapOps) if err != nil { s.logger.Printf("warning: extension registration failed: %v", err) } else {