-
Notifications
You must be signed in to change notification settings - Fork 630
Create a Sidecar for fanning out HTTP requests #435
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
26 commits
Select commit
Hold shift + click to select a range
b745cad
Unit tests for passthrough headers.
Harwayne 0f949d5
Unit tests.
Harwayne fd2ad47
Initial fanout sidecar.
Harwayne b850e29
Switch to ChannelSubscriberSpec.
Harwayne 9e0c7f6
Fanout tests work.
Harwayne b03f91c
MultiChannelFanoutHandler tests work.
Harwayne 6ed11df
Remove ClientFactory.
Harwayne 7de128d
All tests working.
Harwayne b7bdd18
Remove erroneous messages from storage.
Harwayne 5f69090
Run hack/update-deps.sh.
Harwayne b4ec4a0
swappable.Handler is now the 'top' http.Handler. ConfigMaps can be up…
Harwayne 8338ceb
Delete the old config_map_handler.
Harwayne ced872b
Respond to PR comments.
Harwayne 2ba92be
Merge branch 'master' into fanout-with-dispatcher
Harwayne 5be5ea5
Merge branch 'master' into fanout-with-dispatcher
Harwayne 0d1b9ed
Remove the no longer used httpDoer interface.
Harwayne 4ac2f9e
Merge branch 'master' into fanout-with-dispatcher
Harwayne 46a9b13
hack/update-deps.sh
Harwayne d046c88
Use channels to send information between the fanout's receiver and di…
Harwayne 6ecefb3
Symlink VENDOR-LICENSE and LICENSE
Harwayne e82c2bb
Respond to PR comments.
Harwayne bd9facc
Use InformerWatcher rather the homegrown ConfigMap watcher.
Harwayne 1bb5b44
Change filesystem watcher unit tests away from using the HTTP server …
Harwayne 895dcd3
fanout.Handler synchronously calls dispatch(), rather than asynchrono…
Harwayne b7c2bf1
Respond to PR comments.
Harwayne 60247cf
Shutdown with a timeout.
Harwayne File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| ../../../LICENSE |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| ../../../VENDOR-LICENSE |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,167 @@ | ||
| /* | ||
| Copyright 2018 The Knative 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 | ||
|
|
||
| https://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. | ||
| */ | ||
|
|
||
| // A sidecar that implements filtering of Cloud Events sent out via HTTP. Implemented as an HTTP | ||
| // proxy that the main containers need to write through. | ||
|
|
||
| package main | ||
|
|
||
| import ( | ||
| "context" | ||
| "errors" | ||
| "flag" | ||
| "fmt" | ||
| "github.com/knative/eventing/pkg/sidecar/configmap/filesystem" | ||
| "github.com/knative/eventing/pkg/sidecar/configmap/watcher" | ||
| "github.com/knative/eventing/pkg/sidecar/swappable" | ||
| "github.com/knative/eventing/pkg/system" | ||
| "go.uber.org/zap" | ||
| "golang.org/x/sync/errgroup" | ||
| "k8s.io/client-go/kubernetes" | ||
| "log" | ||
| "net/http" | ||
| "sigs.k8s.io/controller-runtime/pkg/client/config" | ||
| "sigs.k8s.io/controller-runtime/pkg/manager" | ||
| "sigs.k8s.io/controller-runtime/pkg/runtime/signals" | ||
| "strings" | ||
| "time" | ||
| ) | ||
|
|
||
| const ( | ||
| defaultConfigMapName = "in-memory-bus-config" | ||
|
|
||
| // The following are the only valid values of the config_map_noticer flag. | ||
| cmnfVolume = "volume" | ||
| cmnfWatcher = "watcher" | ||
| ) | ||
|
|
||
| var ( | ||
| readTimeout = 1 * time.Minute | ||
| writeTimeout = 1 * time.Minute | ||
|
|
||
| port int | ||
| configMapNoticer string | ||
| configMapNamespace string | ||
| configMapName string | ||
| ) | ||
|
|
||
| func init() { | ||
| flag.IntVar(&port, "sidecar_port", -1, "The port to run the sidecar on.") | ||
| flag.StringVar(&configMapNoticer, "config_map_noticer", "", fmt.Sprintf("The system to notice changes to the ConfigMap. Valid values are: %s", configMapNoticerFlags())) | ||
| flag.StringVar(&configMapNamespace, "config_map_namespace", system.Namespace, "The namespace of the ConfigMap that is watched for configuration.") | ||
| flag.StringVar(&configMapName, "config_map_name", defaultConfigMapName, "The name of the ConfigMap that is watched for configuration.") | ||
| } | ||
|
|
||
| func configMapNoticerFlags() string { | ||
| return strings.Join([]string{cmnfVolume, cmnfWatcher}, ", ") | ||
| } | ||
|
|
||
| func main() { | ||
| flag.Parse() | ||
|
|
||
| logger, err := zap.NewProduction() | ||
| if err != nil { | ||
| log.Fatalf("Unable to create logger: %v", err) | ||
| } | ||
|
|
||
| if port < 0 { | ||
| logger.Fatal("--sidecar_port flag must be set") | ||
| } | ||
|
|
||
| sh, err := swappable.NewEmptyHandler(logger) | ||
| if err != nil { | ||
| logger.Fatal("Unable to create swappable.Handler", zap.Error(err)) | ||
| } | ||
|
|
||
| mgr, err := setupConfigMapNoticer(logger, sh.UpdateConfig) | ||
| if err != nil { | ||
| logger.Fatal("Unable to create configMap noticer.", zap.Error(err)) | ||
| } | ||
|
|
||
| s := &http.Server{ | ||
| Addr: fmt.Sprintf(":%d", port), | ||
| Handler: sh, | ||
| ErrorLog: zap.NewStdLog(logger), | ||
| ReadTimeout: readTimeout, | ||
| WriteTimeout: writeTimeout, | ||
| } | ||
|
|
||
| // Start both the manager (which notices ConfigMap changes) and the HTTP server. | ||
| var g errgroup.Group | ||
| g.Go(func() error { | ||
| // set up signals so we handle the first shutdown signal gracefully | ||
| stopCh := signals.SetupSignalHandler() | ||
| // Start blocks forever, so run it in a goroutine. | ||
| return mgr.Start(stopCh) | ||
| }) | ||
| logger.Info("Fanout sidecar Listening...", zap.String("Address", s.Addr)) | ||
| g.Go(s.ListenAndServe) | ||
| err = g.Wait() | ||
| if err != nil { | ||
| logger.Error("Either the HTTP server or the ConfigMap noticer failed.", zap.Error(err)) | ||
| } | ||
|
|
||
| ctx, cancel := context.WithTimeout(context.Background(), writeTimeout) | ||
| defer cancel() | ||
| s.Shutdown(ctx) | ||
| } | ||
|
|
||
| func setupConfigMapNoticer(logger *zap.Logger, configUpdated swappable.UpdateConfig) (manager.Manager, error) { | ||
| mgr, err := manager.New(config.GetConfigOrDie(), manager.Options{}) | ||
| if err != nil { | ||
| return nil, err | ||
| logger.Error("Error starting manager.", zap.Error(err)) | ||
| } | ||
|
|
||
| switch configMapNoticer { | ||
| case cmnfVolume: | ||
| err = setupConfigMapVolume(logger, mgr, configUpdated) | ||
| case cmnfWatcher: | ||
| err = setupConfigMapWatcher(logger, mgr, configUpdated) | ||
| default: | ||
| err = errors.New("need to provide the --config_map_noticer flag") | ||
| } | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
|
|
||
| return mgr, nil | ||
| } | ||
|
|
||
| func setupConfigMapVolume(logger *zap.Logger, mgr manager.Manager, configUpdated swappable.UpdateConfig) error { | ||
| cmn, err := filesystem.NewConfigMapWatcher(logger, filesystem.ConfigDir, configUpdated) | ||
| if err != nil { | ||
| logger.Error("Unable to create filesystem.ConifgMapWatcher", zap.Error(err)) | ||
| return err | ||
| } | ||
| mgr.Add(cmn) | ||
| return nil | ||
| } | ||
|
|
||
| func setupConfigMapWatcher(logger *zap.Logger, mgr manager.Manager, configUpdated swappable.UpdateConfig) error { | ||
| kc, err := kubernetes.NewForConfig(mgr.GetConfig()) | ||
| if err != nil { | ||
| return err | ||
| } | ||
|
|
||
| cmw, err := watcher.NewWatcher(logger, kc, configMapNamespace, configMapName, configUpdated) | ||
| if err != nil { | ||
| return err | ||
| } | ||
|
|
||
| mgr.Add(cmw) | ||
| return nil | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,124 @@ | ||
| /* | ||
| Copyright 2018 The Knative 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 filesystem | ||
|
|
||
| import ( | ||
| "errors" | ||
| "github.com/fsnotify/fsnotify" | ||
| sidecarconfigmap "github.com/knative/eventing/pkg/sidecar/configmap" | ||
| "github.com/knative/eventing/pkg/sidecar/multichannelfanout" | ||
| "github.com/knative/eventing/pkg/sidecar/swappable" | ||
| "github.com/knative/pkg/configmap" | ||
| "go.uber.org/zap" | ||
| ) | ||
|
|
||
| const ( | ||
| // The mount path of the configMap volume. | ||
| ConfigDir = "/etc/config/fanout_sidecar" | ||
| ) | ||
|
|
||
| // Monitors an attached ConfigMap volume for updated configuration and calls `configUpdated` when | ||
| // the value changes. | ||
| type configMapWatcher struct { | ||
| logger *zap.Logger | ||
| // The directory to read the configMap from. | ||
| dir string | ||
| // Stop the watcher by closing this channel. | ||
| watcherStopCh chan<- bool | ||
|
|
||
| // The function to call when the configuration is updated. | ||
| configUpdated swappable.UpdateConfig | ||
| } | ||
|
|
||
| // NewConfigMapWatcher creates a new filesystem.configMapWatcher. The caller is responsible for | ||
| // calling Start(<-chan), likely via a controller-runtime Manager. | ||
| func NewConfigMapWatcher(logger *zap.Logger, dir string, updateConfig swappable.UpdateConfig) (*configMapWatcher, error) { | ||
| conf, err := readConfigMap(logger, dir) | ||
| if err != nil { | ||
| logger.Error("Unable to read configMap", zap.Error(err)) | ||
| return nil, err | ||
| } | ||
|
|
||
| logger.Info("Read initial configMap", zap.Any("conf", conf)) | ||
|
|
||
| err = updateConfig(conf) | ||
| if err != nil { | ||
| logger.Error("Unable to use the initial configMap: %v", zap.Error(err)) | ||
| return nil, err | ||
| } | ||
|
|
||
| cmw := &configMapWatcher{ | ||
| logger: logger, | ||
| dir: dir, | ||
| configUpdated: updateConfig, | ||
| } | ||
| return cmw, nil | ||
| } | ||
|
|
||
| // readConfigMap attempts to read the configMap from the attached volume. | ||
| func readConfigMap(logger *zap.Logger, dir string) (*multichannelfanout.Config, error) { | ||
| cm, err := configmap.Load(dir) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
| return sidecarconfigmap.NewFanoutConfig(logger, cm) | ||
| } | ||
|
|
||
| // updateConfig reads the configMap data and calls `configUpdated` with the updated value. | ||
| func (cmw *configMapWatcher) updateConfig() { | ||
| conf, err := readConfigMap(cmw.logger, cmw.dir) | ||
| if err != nil { | ||
| cmw.logger.Error("Unable to read the configMap", zap.Error(err)) | ||
| return | ||
| } | ||
| err = cmw.configUpdated(conf) | ||
| if err != nil { | ||
| cmw.logger.Error("Unable to update config", zap.Error(err)) | ||
| return | ||
| } | ||
| } | ||
|
|
||
| func (cmw *configMapWatcher) Start(stopCh <-chan struct{}) error { | ||
| watcher, err := fsnotify.NewWatcher() | ||
| if err != nil { | ||
| return err | ||
| } | ||
|
|
||
| err = watcher.Add(cmw.dir) | ||
| if err != nil { | ||
| return err | ||
| } | ||
|
|
||
| for { | ||
| select { | ||
| case _, ok := <-watcher.Events: | ||
| if !ok { | ||
| // Channel closed. | ||
| return errors.New("watcher.Events channel closed") | ||
| } | ||
| cmw.updateConfig() | ||
| case err, ok := <-watcher.Errors: | ||
| if !ok { | ||
| // Channel closed. | ||
| return errors.New("watcher.Errors channel closed") | ||
| } | ||
| cmw.logger.Error("watcher.Errors", zap.Error(err)) | ||
| case <-stopCh: | ||
| return watcher.Close() | ||
| } | ||
| } | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.