Skip to content
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

Query a remote Elasticsearch for License and features #7946

Merged
merged 2 commits into from
Aug 29, 2018

Conversation

ph
Copy link
Contributor

@ph ph commented Aug 10, 2018

Implements a License manager inside beats, as we development more
features that depends on the licensing and the capabilities of a remote
cluster we need a unique way to access that information. This commit
implements the following:

Add a License manager that can be started at the beginning of the beats
instance initialization. The manager takes a fetcher, currently we only
support Elasticsearch as the license backend but we could add support
for an Logstash endpoint that could proxy the license.

Notes:

  • By default when the manager is started, no license is available,
    calling Get() on the manager will return a license not found.

  • The manager will periodically retrieve the license from the fetcher.

  • When an error occurs on the periodic check, the license wont be
    invalidated right away but will enter a grace period, after this period
    the license will be invalidated and will replaced by the OSS license.

  • License and capabilities and be retrieved by calling Get() or
    registering a type implementing the Watcher interface.

@ph ph added in progress Pull request is currently in progress. libbeat labels Aug 10, 2018
// Fetch connect to the cluster and call the xpack api parses the data and return an Info object.
func (f *ElasticFetcher) Fetch() (*License, error) {
status, body, err := f.client.Request("GET", xPackURL, "", params, nil)
if status != statusOK {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the license is Apache, it will return a 405 here as the endpoint does not exist.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, I was not sure of the behavior in the OSS world :)

@ph ph changed the title [WIP] License checker to use with x-pack. [WIP] Query a remote Elasticsearch for License and capabilities Aug 13, 2018
@ph
Copy link
Contributor Author

ph commented Aug 13, 2018

A few questions ported from #7953

Open questions:
Should we check for expiration?
I only tested with trial and basic, is the license type for gold and platinum
Is trial always platinum license?
What does the API endpoint look like if the trial is expired?

@ruflin ruflin mentioned this pull request Aug 14, 2018
@ph ph force-pushed the feature/license-check branch 2 times, most recently from a867016 to e26b92c Compare August 23, 2018 17:44
@ph ph changed the title [WIP] Query a remote Elasticsearch for License and capabilities Query a remote Elasticsearch for License and features Aug 23, 2018
@ph ph added review and removed in progress Pull request is currently in progress. labels Aug 23, 2018
@ph
Copy link
Contributor Author

ph commented Aug 23, 2018

This code is ready for review.

@ph
Copy link
Contributor Author

ph commented Aug 23, 2018

🤦‍♂️ forgot to run the test with the data race on.

}

// NewWithFetcher takes a fetcher and return a license manager.
func NewWithFetcher(fetcher Fetcher, duration time.Duration, gracePeriod time.Duration) *Manager {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I should pass a Setting Object here so we can define the duration, grace period and anything related to the backoff.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you thinking of config options that are also exposed to the user?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we need to expose any options to the end users, they could put a really large refresh delay.. which is probably not what we want.

Copy link
Contributor

@ruflin ruflin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Could we add some integration tests with Elasticsearch search. Even if we only test if basic is enabled it will tell us very quickly in case something in the API endpoint for format changed on the ES side.
  • How does this handle trial licenses? I wonder if that is something we should fetch and at least report in the logs. This could make debugging easier when we see in the logs it's a trial and the user tells us it was working and then stopped ...

I didn't check all the code in detail more functionality wise so would be good if someone else has a more detailed look.

}
s := string(b[1 : len(b)-1])
license, ok := licenseLookup[s]
if ok {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Normally we check for the error an return the error inside the if clause and the flow to the end of the method is the non error flow.


s := string(b[1 : len(b)-1])
state, ok := stateLookup[s]
if ok {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See comment above


t.Run("200 response", func(t *testing.T) {
h := func(w http.ResponseWriter, r *http.Request) {
json, err := ioutil.ReadFile("data/xpack.json")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we make this data/xpack-6.4.1.json (or whatever version it is) and then run a glob here? This makes it easy to add more test files for different versions in the future.

It allows us to also add files from older ES versions.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

++ love it.

Monitoring monitoring `json:"monitoring"`
Rollup rollup `json:"rollup"`
Security security `json:"security"`
Watcher watcher `json:"watcher"`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we track in here all the features or only the ones that Beats actually uses / requires?

Copy link
Contributor Author

@ph ph Aug 24, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've exposed the current features, only Graph isn't used is the less relevant for us, but we can revisit that when a new feature is exposed via elasticsearch.

}

// NewWithFetcher takes a fetcher and return a license manager.
func NewWithFetcher(fetcher Fetcher, duration time.Duration, gracePeriod time.Duration) *Manager {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you thinking of config options that are also exposed to the user?

// Stop terminates the license manager, the go routine will be stopped and the cached license will
// be removed and no more checks can be done on the manager.
func (m *Manager) Stop() {
defer m.log.Info("license manager stopped")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we have a lock on stop to make sure it's not called in the middle of a worker starting for example?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a valid case, let me make a change.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I refactored Stop so the shutdown is more explicit and sync with the worker, this makes the behavior of stopping the manager predictable and the callbacks are calling order is more sane.

}

func (m *Manager) worker() {
m.log.Debug("starting periodic updater")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could you mention in the log line that this is the period license check

func NewWithFetcher(fetcher Fetcher, duration time.Duration, gracePeriod time.Duration) *Manager {
m := &Manager{
fetcher: fetcher,
duration: duration,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you expect as a default duration?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a good question, not sure what would be the best timing for that.
I've looked at Logstash and they refresh every 30sec, https://github.com/elastic/logstash/blob/master/x-pack/lib/license_checker/licensed.rb#L34-L42

Maybe every minute would be a good default, WDYT?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

30s sounds like a bit too much TBH. If we have 100'000 Beats that would mean 100'000 additional requests every 30s. In most cases I would not expect the license to change so I was more thinking of something like once a day.

If someone is having a trial license, we should probably check more often.

Perhaps we could do something like "check once per day" and but also check every time on reconnect. Alternative we could back off over time or make it more random?

One thing that would be nice if in the future with central management it could be triggered there that a license check should happen on all Beats.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe the current monitoring elasticsearch reporter check every 30 seconds, I understand having a 100k beats could increase the load on the server.

A check every few hours should be fine because I find once a day a bit too infrequent, I will think about saner defaults

Copy link

@urso urso Aug 27, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

monitoring only checks every 30s on init. After license is found it won't update license information. but then, for monitoring it's not a license check, but test if features+API endpoint is available in ES.

Some randomized backoff on initial check would be nice. Once license data are available, we don't need to check now. We could also cache license check results, so to reduce checks even between restarts. Maybe we can add a check to the ES output triggering a license checks once it finds the version is updated?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will add some initial jitter at startup.

Maybe we can add a check to the ES output triggering a license checks once it finds the version is updated?

I will create a followup issue for that.

Copy link
Member

@andrewkroh andrewkroh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a need to inspect licenses from OSS Beats? If not, then perhaps this belongs be in x-pack/?

@ph
Copy link
Contributor Author

ph commented Aug 24, 2018

Is there a need to inspect licenses from OSS Beats? If not, then perhaps this belongs be in x-pack/?

@andrewkroh It also check for features, so we can refactor the monitoring code to use the same license checker instead of using the onConnect handler from the elasticsearch client so we only have one way to check it.

But this is will be mostly used by Beatless and config management at first.

@ph
Copy link
Contributor Author

ph commented Aug 24, 2018

Could we add some integration tests with Elasticsearch search. Even if we only test if basic is enabled it will tell us very quickly in case something in the API endpoint for format changed on the ES side.

I have added a simple integration test for the '_xpack' endpoint, this is only a sanity check.

How does this handle trial licenses? I wonder if that is something we should fetch and at least report in the logs. This could make debugging easier when we see in the logs it's a trial and the user tells us it was working and then stopped ...

Do we still have a trial license type returned? I am not sure I will take a deeper look.

@ph ph requested a review from urso August 24, 2018 15:58
@ruflin
Copy link
Contributor

ruflin commented Aug 27, 2018

I will need this feature to be in OSS for ILM.

@ph
Copy link
Contributor Author

ph commented Aug 27, 2018

Adding Trial response for the LicenseType

Copy link

@urso urso left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change LGTM. Would be nice to have some go based integration tests (e.g. fetcher_integration_test.go + build tag integration).

@ph
Copy link
Contributor Author

ph commented Aug 27, 2018

@urso we have a sanity check there https://github.com/elastic/beats/pull/7946/files#diff-52042abe2d3906a09d82626f97c7ab29R1 :)

I was surprised how easy it was, it also catched one bug.

// Manager keeps tracks of license management, it uses a fetcher usually the ElasticFetcher to
// retrieve a licence from a specific cluster.
//
// Starting the manager will starts a go routine to periodically query the license fetcher.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/will starts/will start/

// retrieve a licence from a specific cluster.
//
// Starting the manager will starts a go routine to periodically query the license fetcher.
// if an error occur on the fetcher we will retries until we successfully
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/will retries/will retry/

ph added 2 commits August 29, 2018 12:49
Implements a License manager inside beats, as we development more
features that depends on the licensing and the capabilities of a remote
cluster we need a unique way to access that information. This commit
implements the following:

Add a License manager that can be started at the beginning of the beats
instance initialization. The manager takes a fetcher, currently we only
support Elasticsearch as the license backend but we could add support
for an Logstash endpoint that could proxy the license.

Notes:

- By default when the manager is started, no license is available,
calling `Get()` on the manager will return a license not found.

- The manager will periodically retrieve the license from the fetcher.

- When an error occurs on the periodic check, the license wont be
invalidated right away but will enter a grace period, after this period
the license will be invalidated and will replaced by the OSS license.

- License and capabilities and be retrieved by calling `Get()` or
registering a type implementing the `Watcher` interface.
@ph ph force-pushed the feature/license-check branch from 1384ec8 to b0d4ca4 Compare August 29, 2018 16:50
@ph ph changed the base branch from master to feature/beatless August 29, 2018 16:56
@ph ph force-pushed the feature/beatless branch from 0d58e1d to c9ab9c3 Compare August 29, 2018 16:58
@ph ph merged commit ff6073a into elastic:feature/beatless Aug 29, 2018
@ph ph deleted the feature/license-check branch August 29, 2018 18:03
ph added a commit to ph/beats that referenced this pull request Sep 10, 2018
* License manager

Implements a License manager inside beats, as we development more
features that depends on the licensing and the capabilities of a remote
cluster we need a unique way to access that information. This commit
implements the following:

Add a License manager that can be started at the beginning of the beats
instance initialization. The manager takes a fetcher, currently we only
support Elasticsearch as the license backend but we could add support
for an Logstash endpoint that could proxy the license.

Notes:

- By default when the manager is started, no license is available,
calling `Get()` on the manager will return a license not found.

- The manager will periodically retrieve the license from the fetcher.

- When an error occurs on the periodic check, the license wont be
invalidated right away but will enter a grace period, after this period
the license will be invalidated and will replaced by the OSS license.

- License and capabilities and be retrieved by calling `Get()` or
registering a type implementing the `Watcher` interface.
ph added a commit that referenced this pull request Sep 25, 2018
* License manager

Implements a License manager inside beats, as we development more
features that depends on the licensing and the capabilities of a remote
cluster we need a unique way to access that information. This commit
implements the following:

Add a License manager that can be started at the beginning of the beats
instance initialization. The manager takes a fetcher, currently we only
support Elasticsearch as the license backend but we could add support
for an Logstash endpoint that could proxy the license.

Notes:

- By default when the manager is started, no license is available,
calling `Get()` on the manager will return a license not found.

- The manager will periodically retrieve the license from the fetcher.

- When an error occurs on the periodic check, the license wont be
invalidated right away but will enter a grace period, after this period
the license will be invalidated and will replaced by the OSS license.

- License and capabilities and be retrieved by calling `Get()` or
registering a type implementing the `Watcher` interface.
ph added a commit that referenced this pull request Sep 28, 2018
* License manager

Implements a License manager inside beats, as we development more
features that depends on the licensing and the capabilities of a remote
cluster we need a unique way to access that information. This commit
implements the following:

Add a License manager that can be started at the beginning of the beats
instance initialization. The manager takes a fetcher, currently we only
support Elasticsearch as the license backend but we could add support
for an Logstash endpoint that could proxy the license.

Notes:

- By default when the manager is started, no license is available,
calling `Get()` on the manager will return a license not found.

- The manager will periodically retrieve the license from the fetcher.

- When an error occurs on the periodic check, the license wont be
invalidated right away but will enter a grace period, after this period
the license will be invalidated and will replaced by the OSS license.

- License and capabilities and be retrieved by calling `Get()` or
registering a type implementing the `Watcher` interface.
ph added a commit that referenced this pull request Oct 4, 2018
* License manager

Implements a License manager inside beats, as we development more
features that depends on the licensing and the capabilities of a remote
cluster we need a unique way to access that information. This commit
implements the following:

Add a License manager that can be started at the beginning of the beats
instance initialization. The manager takes a fetcher, currently we only
support Elasticsearch as the license backend but we could add support
for an Logstash endpoint that could proxy the license.

Notes:

- By default when the manager is started, no license is available,
calling `Get()` on the manager will return a license not found.

- The manager will periodically retrieve the license from the fetcher.

- When an error occurs on the periodic check, the license wont be
invalidated right away but will enter a grace period, after this period
the license will be invalidated and will replaced by the OSS license.

- License and capabilities and be retrieved by calling `Get()` or
registering a type implementing the `Watcher` interface.
ph added a commit that referenced this pull request Oct 18, 2018
* License manager

Implements a License manager inside beats, as we development more
features that depends on the licensing and the capabilities of a remote
cluster we need a unique way to access that information. This commit
implements the following:

Add a License manager that can be started at the beginning of the beats
instance initialization. The manager takes a fetcher, currently we only
support Elasticsearch as the license backend but we could add support
for an Logstash endpoint that could proxy the license.

Notes:

- By default when the manager is started, no license is available,
calling `Get()` on the manager will return a license not found.

- The manager will periodically retrieve the license from the fetcher.

- When an error occurs on the periodic check, the license wont be
invalidated right away but will enter a grace period, after this period
the license will be invalidated and will replaced by the OSS license.

- License and capabilities and be retrieved by calling `Get()` or
registering a type implementing the `Watcher` interface.
ph added a commit that referenced this pull request Oct 24, 2018
* License manager

Implements a License manager inside beats, as we development more
features that depends on the licensing and the capabilities of a remote
cluster we need a unique way to access that information. This commit
implements the following:

Add a License manager that can be started at the beginning of the beats
instance initialization. The manager takes a fetcher, currently we only
support Elasticsearch as the license backend but we could add support
for an Logstash endpoint that could proxy the license.

Notes:

- By default when the manager is started, no license is available,
calling `Get()` on the manager will return a license not found.

- The manager will periodically retrieve the license from the fetcher.

- When an error occurs on the periodic check, the license wont be
invalidated right away but will enter a grace period, after this period
the license will be invalidated and will replaced by the OSS license.

- License and capabilities and be retrieved by calling `Get()` or
registering a type implementing the `Watcher` interface.
ph added a commit that referenced this pull request Oct 24, 2018
* License manager

Implements a License manager inside beats, as we development more
features that depends on the licensing and the capabilities of a remote
cluster we need a unique way to access that information. This commit
implements the following:

Add a License manager that can be started at the beginning of the beats
instance initialization. The manager takes a fetcher, currently we only
support Elasticsearch as the license backend but we could add support
for an Logstash endpoint that could proxy the license.

Notes:

- By default when the manager is started, no license is available,
calling `Get()` on the manager will return a license not found.

- The manager will periodically retrieve the license from the fetcher.

- When an error occurs on the periodic check, the license wont be
invalidated right away but will enter a grace period, after this period
the license will be invalidated and will replaced by the OSS license.

- License and capabilities and be retrieved by calling `Get()` or
registering a type implementing the `Watcher` interface.
ruflin added a commit to ruflin/beats that referenced this pull request Dec 5, 2018
This adds support to Beats for index lifecycle management. The PR is currently work in progress. It works against a snapshot build of Elasticsearch from the ILM branch and the predefined configs in the metricbeat.yml file.

*Blocked*

This PR is currently blocked as the Elasticsearch ilm branch is not yet merged into master. Also the license checker in elastic#7946 needs to be merged first.

*TODO*

* [x] Add License Check elastic#7946
* [x] Add ES version check
* [x] Finish implementation of setup command for policy and test it
* [ ] Add tests
* [x] Add docs
* [x] Check all todos

For more details see elastic#7935

To test this feature, Metricbeat can be run as following: `./metricbeat -e -E output.elasticsearch.ilm.enabled=true`. It currently also requires a version of Elasticsearch from the branch `index-lifecycle` to be running.

The policy loaded looks as following:

```
{
    "policy": {
        "type": "timeseries",
        "phases": {
            "hot": {
                "actions": {
                    "rollover": {
                        "max_size": "25gb",
                        "max_age":   "30d"
                    }
                }
            }
        }
    }
}
```
@ph ph added the Functionbeat label Jan 8, 2019
leweafan pushed a commit to leweafan/beats that referenced this pull request Apr 28, 2023
* License manager

Implements a License manager inside beats, as we development more
features that depends on the licensing and the capabilities of a remote
cluster we need a unique way to access that information. This commit
implements the following:

Add a License manager that can be started at the beginning of the beats
instance initialization. The manager takes a fetcher, currently we only
support Elasticsearch as the license backend but we could add support
for an Logstash endpoint that could proxy the license.

Notes:

- By default when the manager is started, no license is available,
calling `Get()` on the manager will return a license not found.

- The manager will periodically retrieve the license from the fetcher.

- When an error occurs on the periodic check, the license wont be
invalidated right away but will enter a grace period, after this period
the license will be invalidated and will replaced by the OSS license.

- License and capabilities and be retrieved by calling `Get()` or
registering a type implementing the `Watcher` interface.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants