Skip to content

Commit

Permalink
Add the httpsprovider component (#6683)
Browse files Browse the repository at this point in the history
* Feat: add httpsprovider component

Add the httpsprovider component that allows the collector to fetch
configuration from a web server using the https protocol.

Signed-off-by: Raphael Silva <[email protected]>

* Add httpsprovider to the list of config providers

Signed-off-by: Raphael Silva <[email protected]>

* Update README

Signed-off-by: Raphael Silva <[email protected]>

* Apply suggestions from code review

Co-authored-by: Pablo Baeyens <[email protected]>

* Improve unit tests

Improve unit tests to check for error while parsing test URL.

Co-authored-by: Pablo Baeyens <[email protected]>

* Fix comments

Co-authored-by: Anthony Mirabella <[email protected]>

* Fix comments

Co-authored-by: Anthony Mirabella <[email protected]>

* Use SchemeType instead of TransportType

Signed-off-by: Raphael Silva <[email protected]>

* Refactor to improve readability

Signed-off-by: Raphael Silva <[email protected]>

* Use consts from http to set http status code

Signed-off-by: Raphael Silva <[email protected]>

* Refactor code organization

Signed-off-by: Raphael Silva <[email protected]>

* Use struct with zero initialized properties

Signed-off-by: Raphael Silva <[email protected]>

Signed-off-by: Raphael Silva <[email protected]>
Co-authored-by: Pablo Baeyens <[email protected]>
Co-authored-by: Anthony Mirabella <[email protected]>
  • Loading branch information
3 people authored Jan 25, 2023
1 parent f616fab commit 6f1cbd8
Show file tree
Hide file tree
Showing 10 changed files with 545 additions and 145 deletions.
16 changes: 16 additions & 0 deletions .chloggen/add-https-httpprovider.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
change_type: new_component

# The name of the component, or a single word describing the area of concern, (e.g. otlpreceiver)
component: httpsprovider

# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
note: Add the httpsprovider. This component allows the collector to fetch configurations from web servers using the HTTPS protocol.

# One or more tracking issues or pull requests related to the change
issues: [6683]

# (Optional) One or more lines of additional information to render under the primary note.
# These lines will be padded with 2 spaces and then inserted directly into the document.
# Use pipe (|) for multiline entries.
subtext:
61 changes: 5 additions & 56 deletions confmap/provider/httpprovider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,66 +15,15 @@
package httpprovider // import "go.opentelemetry.io/collector/confmap/provider/httpprovider"

import (
"context"
"fmt"
"io"
"net/http"
"strings"

"go.opentelemetry.io/collector/confmap"
"go.opentelemetry.io/collector/confmap/provider/internal"
)

const (
schemeName = "http"
"go.opentelemetry.io/collector/confmap/provider/internal/configurablehttpprovider"
)

type provider struct {
client http.Client
}

// New returns a new confmap.Provider that reads the configuration from a file.
//
// This Provider supports "http" scheme, and can be called with a "uri" that follows:
// New returns a new confmap.Provider that reads the configuration from a http server.
//
// One example for http-uri be like: http://localhost:3333/getConfig
// This Provider supports "http" scheme.
//
// Examples:
// `http://localhost:3333/getConfig` - (unix, windows)
// One example for HTTP URI is: http://localhost:3333/getConfig
func New() confmap.Provider {
return &provider{client: http.Client{}}
}

func (fmp *provider) Retrieve(_ context.Context, uri string, _ confmap.WatcherFunc) (*confmap.Retrieved, error) {
if !strings.HasPrefix(uri, schemeName+":") {
return nil, fmt.Errorf("%q uri is not supported by %q provider", uri, schemeName)
}

// send a HTTP GET request
resp, err := fmp.client.Get(uri)
if err != nil {
return nil, fmt.Errorf("unable to download the file via HTTP GET for uri %q, with err: %w ", uri, err)
}
defer resp.Body.Close()

// check the HTTP status code
if resp.StatusCode != 200 {
return nil, fmt.Errorf("404: resource didn't exist, fail to read the response body from uri %q", uri)
}

// read the response body
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("fail to read the response body from uri %q, with err: %w ", uri, err)
}

return internal.NewRetrievedFromYAML(body)
}

func (*provider) Scheme() string {
return schemeName
}

func (*provider) Shutdown(context.Context) error {
return nil
return configurablehttpprovider.New(configurablehttpprovider.HTTPScheme)
}
89 changes: 1 addition & 88 deletions confmap/provider/httpprovider/provider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,101 +16,14 @@ package httpprovider

import (
"context"
"fmt"
"net/http"
"net/http/httptest"
"os"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"go.opentelemetry.io/collector/confmap/confmaptest"
)

func TestFunctionalityDownloadFileHTTP(t *testing.T) {
fp := New()
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
f, err := os.ReadFile("./testdata/otel-config.yaml")
if err != nil {
w.WriteHeader(404)
_, innerErr := w.Write([]byte("Cannot find the config file"))
if innerErr != nil {
fmt.Println("Write failed: ", innerErr)
}
return
}
w.WriteHeader(200)
_, err = w.Write(f)
if err != nil {
fmt.Println("Write failed: ", err)
}
}))
defer ts.Close()
_, err := fp.Retrieve(context.Background(), ts.URL, nil)
assert.NoError(t, err)
assert.NoError(t, fp.Shutdown(context.Background()))
}

func TestUnsupportedScheme(t *testing.T) {
fp := New()
_, err := fp.Retrieve(context.Background(), "https://...", nil)
assert.Error(t, err)
assert.NoError(t, fp.Shutdown(context.Background()))
}

func TestEmptyURI(t *testing.T) {
fp := New()
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(400)
}))
defer ts.Close()
_, err := fp.Retrieve(context.Background(), ts.URL, nil)
require.Error(t, err)
require.NoError(t, fp.Shutdown(context.Background()))
}

func TestRetrieveFromShutdownServer(t *testing.T) {
fp := New()
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}))
ts.Close()
_, err := fp.Retrieve(context.Background(), ts.URL, nil)
assert.Error(t, err)
require.NoError(t, fp.Shutdown(context.Background()))
}

func TestNonExistent(t *testing.T) {
fp := New()
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(404)
}))
defer ts.Close()
_, err := fp.Retrieve(context.Background(), ts.URL, nil)
assert.Error(t, err)
require.NoError(t, fp.Shutdown(context.Background()))
}

func TestInvalidYAML(t *testing.T) {
fp := New()
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(200)
_, err := w.Write([]byte("wrong : ["))
if err != nil {
fmt.Println("Write failed: ", err)
}
}))
defer ts.Close()
_, err := fp.Retrieve(context.Background(), ts.URL, nil)
assert.Error(t, err)
require.NoError(t, fp.Shutdown(context.Background()))
}

func TestScheme(t *testing.T) {
func TestSupportedScheme(t *testing.T) {
fp := New()
assert.Equal(t, "http", fp.Scheme())
require.NoError(t, fp.Shutdown(context.Background()))
}

func TestValidateProviderScheme(t *testing.T) {
assert.NoError(t, confmaptest.ValidateProviderScheme(New()))
}
18 changes: 18 additions & 0 deletions confmap/provider/httpsprovider/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
### What is the httpsprovider?

An implementation of `confmap.Provider` for HTTPS (httpsprovider) allows OTEL Collector to use the HTTPS protocol to
load configuration files stored in web servers.

Expected URI format:
- https://...

### Prerequistes

You need to setup a HTTP server with support to HTTPS. The server must have a certificate that can be validated in the
host running the collector using system root certificates.

### Configuration

At this moment, this component only support communicating with servers whose certificate can be verified using the root
CA certificates installed in the system. The process of adding more root CA certificates to the system is operating
system dependent. For Linux, please refer to the `update-ca-trust` command.
30 changes: 30 additions & 0 deletions confmap/provider/httpsprovider/provider.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Copyright The OpenTelemetry 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 httpsprovider // import "go.opentelemetry.io/collector/confmap/provider/httpsprovider"

import (
"go.opentelemetry.io/collector/confmap"
"go.opentelemetry.io/collector/confmap/provider/internal/configurablehttpprovider"
)

// New returns a new confmap.Provider that reads the configuration from a https server.
//
// This Provider supports "https" scheme. One example of an HTTPS URI is: https://localhost:3333/getConfig
//
// To add extra CA certificates you need to install certificates in the system pool. This procedure is operating system
// dependent. E.g.: on Linux please refer to the `update-ca-trust` command.
func New() confmap.Provider {
return configurablehttpprovider.New(configurablehttpprovider.HTTPSScheme)
}
26 changes: 26 additions & 0 deletions confmap/provider/httpsprovider/provider_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Copyright The OpenTelemetry 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 httpsprovider

import (
"testing"

"github.com/stretchr/testify/assert"
)

func TestSupportedScheme(t *testing.T) {
fp := New()
assert.Equal(t, "https", fp.Scheme())
}
Loading

0 comments on commit 6f1cbd8

Please sign in to comment.