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

providers: add scaleway #1794

Merged
merged 2 commits into from
Feb 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/release-notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ nav_order: 9

### Features

- Support Scaleway


### Changes
Expand Down
1 change: 1 addition & 0 deletions docs/supported-platforms.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ Ignition is currently only supported for the following platforms:
* [Equinix Metal] (`packet`) - Ignition will read its configuration from the instance userdata. Cloud SSH keys are handled separately.
* [IBM Power Systems Virtual Server] (`powervs`) - Ignition will read its configuration from the instance userdata. Cloud SSH keys are handled separately.
* [QEMU] (`qemu`) - Ignition will read its configuration from the 'opt/com.coreos/config' key on the QEMU Firmware Configuration Device (available in QEMU 2.4.0 and higher).
* [Scaleway] (`scaleway`) - Ignition will read its configuration from the instance userdata. Cloud SSH keys are handled separately.
* [VirtualBox] (`virtualbox`) - Use the VirtualBox guest property `/Ignition/Config` to provide the config to the virtual machine.
* [VMware] (`vmware`) - Use the VMware Guestinfo variables `ignition.config.data` and `ignition.config.data.encoding` to provide the config and its encoding to the virtual machine. Valid encodings are "", "base64", and "gzip+base64". Guestinfo variables can be provided directly or via an OVF environment, with priority given to variables specified directly.
* [Vultr] (`vultr`) - Ignition will read its configuration from the instance userdata. Cloud SSH keys are handled separately.
Expand Down
62 changes: 62 additions & 0 deletions internal/providers/scaleway/scaleway.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// Copyright 2024 CoreOS, Inc.
//
// 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.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Just a small nit, would it be possible to describe how scale way is fetching the data in a comment above. similar to how it was done here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Comment added. ✔️

// The Scaleway provider fetches a remote configuration from the Scaleway
// user-data metadata service URL.
// NOTE: For security reason, Scaleway requires to query user data with a source port below 1024.

package scaleway

import (
"math/rand"
"net/url"

"github.com/coreos/ignition/v2/config/v3_5_experimental/types"
"github.com/coreos/ignition/v2/internal/platform"
"github.com/coreos/ignition/v2/internal/providers/util"
"github.com/coreos/ignition/v2/internal/resource"

"github.com/coreos/vcontext/report"
)

var (
userdataURL = url.URL{
Scheme: "http",
Host: "169.254.42.42",
Path: "user_data/cloud-init",
}
)

func init() {
platform.Register(platform.Provider{
Name: "scaleway",
Fetch: fetchConfig,
})
}

func fetchConfig(f *resource.Fetcher) (types.Config, report.Report, error) {
// For security reason, Scaleway requires to query user data with a source port below 1024.
port := func() int {
return rand.Intn(1022) + 1
}

data, err := f.FetchToBuffer(userdataURL, resource.FetchOptions{
LocalPort: port,
})
if err != nil && err != resource.ErrNotFound {
return types.Config{}, report.Report{}, err
}

return util.ParseConfig(f.Logger, data)
}
1 change: 1 addition & 0 deletions internal/register/providers.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import (
_ "github.com/coreos/ignition/v2/internal/providers/packet"
_ "github.com/coreos/ignition/v2/internal/providers/powervs"
_ "github.com/coreos/ignition/v2/internal/providers/qemu"
_ "github.com/coreos/ignition/v2/internal/providers/scaleway"
_ "github.com/coreos/ignition/v2/internal/providers/virtualbox"
_ "github.com/coreos/ignition/v2/internal/providers/vmware"
_ "github.com/coreos/ignition/v2/internal/providers/vultr"
Expand Down
28 changes: 28 additions & 0 deletions internal/resource/url.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,12 @@ import (
"fmt"
"hash"
"io"
"net"
"net/http"
"net/url"
"os"
"strings"
"syscall"
"time"

"cloud.google.com/go/compute/metadata"
Expand Down Expand Up @@ -125,6 +127,10 @@ type FetchOptions struct {
// HTTPVerb is an HTTP request method to indicate the desired action to
// be performed for a given resource.
HTTPVerb string

// LocalPort is a function returning a local port used to establish the TCP connection.
// Most of the time, letting the Kernel choose a random port is enough.
LocalPort func() int
jlebon marked this conversation as resolved.
Show resolved Hide resolved
}

// FetchToBuffer will fetch the given url into a temporary file, and then read
Expand Down Expand Up @@ -287,6 +293,28 @@ func (f *Fetcher) fetchFromHTTP(u url.URL, dest io.Writer, opts FetchOptions) er
}
}

if opts.LocalPort != nil {
var (
d net.Dialer
p int
)

// Assert that the port is not already used.
for {
p = opts.LocalPort()
l, err := net.Listen("tcp4", fmt.Sprintf(":%d", p))
if err != nil && errors.Is(err, syscall.EADDRINUSE) {
continue
jlebon marked this conversation as resolved.
Show resolved Hide resolved
} else if err == nil {
l.Close()
break
}
}
d.LocalAddr = &net.TCPAddr{Port: p}

f.client.transport.DialContext = d.DialContext
}

// We do not want to redirect HTTP headers
f.client.client.CheckRedirect = func(req *http.Request, via []*http.Request) error {
req.Header = make(http.Header)
Expand Down
Loading