Skip to content

Commit

Permalink
feat: implement resource API in Talos
Browse files Browse the repository at this point in the history
This brings in `os-runtime` package and exposes resources with first
iteration of read-only API.

Two Talos resources (and one controller) are implemented:

* legacy.Service resource tracks Talos 'service' `RUNNING` state
* config.V1Alpha1 stores current runtime config

Glue point between existing runtime and new os-runtime based runtime is
in `v1alpha2` implementation and `V1Alpha2()` sub-interfaces of existing
`Runtime`, `State`, `Controller` interfaces.

Signed-off-by: Andrey Smirnov <smirnov.andrey@gmail.com>
  • Loading branch information
smira authored and talos-bot committed Jan 19, 2021
1 parent e9aa494 commit 11863dd
Show file tree
Hide file tree
Showing 37 changed files with 2,704 additions and 19 deletions.
5 changes: 5 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@ COPY ./api/network/network.proto /api/network/network.proto
RUN protoc -I/api -I/api/vendor/ --go_out=plugins=grpc,paths=source_relative:/api network/network.proto
COPY ./api/cluster/cluster.proto /api/cluster/cluster.proto
RUN protoc -I/api -I/api/vendor/ --go_out=plugins=grpc,paths=source_relative:/api cluster/cluster.proto
COPY ./api/resource/resource.proto /api/resource/resource.proto
RUN protoc -I/api -I/api/vendor/ --go_out=plugins=grpc,paths=source_relative:/api resource/resource.proto
# Gofumports generated files to adjust import order
RUN gofumports -w -local github.com/talos-systems/talos /api/

Expand All @@ -109,6 +111,7 @@ COPY --from=generate-build /api/time/*.pb.go /pkg/machinery/api/time/
COPY --from=generate-build /api/network/*.pb.go /pkg/machinery/api/network/
COPY --from=generate-build /api/cluster/*.pb.go /pkg/machinery/api/cluster/
COPY --from=generate-build /api/storage/*.pb.go /pkg/machinery/api/storage/
COPY --from=generate-build /api/resource/*.pb.go /pkg/machinery/api/resource/
COPY --from=generate-build /pkg/machinery/config/types/v1alpha1/*_doc.go /pkg/machinery/config/types/v1alpha1/

# The base target provides a container that can be used to build all Talos
Expand Down Expand Up @@ -644,6 +647,7 @@ RUN protoc \
-I/protos/health \
-I/protos/machine \
-I/protos/network \
-I/protos/resource \
-I/protos/security \
-I/protos/storage \
-I/protos/time \
Expand All @@ -654,6 +658,7 @@ RUN protoc \
/protos/health/*.proto \
/protos/machine/*.proto \
/protos/network/*.proto \
/protos/resource/*.proto \
/protos/security/*.proto \
/protos/storage/*.proto \
/protos/time/*.proto
Expand Down
84 changes: 84 additions & 0 deletions api/resource/resource.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
syntax = "proto3";

package resource;

option go_package = "github.com/talos-systems/talos/pkg/machinery/api/resource";
option java_multiple_files = true;
option java_outer_classname = "ResourceApi";
option java_package = "com.resource.api";

import "common/common.proto";

// The resource service definition.
//
// ResourceService provides user-facing API for the Talos resources.
service ResourceService {
rpc Get(GetRequest) returns (GetResponse);
rpc List(ListRequest) returns (stream ListResponse);
rpc Watch(WatchRequest) returns (stream WatchResponse);
}

// common resource definition

message Resource {
Metadata metadata = 1;
Spec spec = 2;
}

message Metadata {
string namespace = 1;
string type = 2;
string id = 3;
string version = 4;
string phase = 5;
repeated string finalizers = 6;
}

message Spec {
bytes yaml = 1;
}

// rpc Get
message GetRequest {
string namespace = 1;
string type = 2;
string id = 3;
}

// The GetResponse message contains the Resource returned.
message Get {
common.Metadata metadata = 1;
Resource definition = 2;
Resource resource = 3;
}

message GetResponse { repeated Get messages = 1; }

// rpc List
// The ListResponse message contains the Resource returned.
message ListRequest {
string namespace = 1;
string type = 2;
}

message ListResponse {
common.Metadata metadata = 1;
Resource definition = 2;
Resource resource = 3;
}

// rpc Watch
// The WatchResponse message contains the Resource returned.
message WatchRequest {}

enum EventType {
CREATED = 0;
UPDATED = 1;
DELETED = 2;
}

message WatchResponse {
common.Metadata metadata = 1;
EventType event_type = 2;
Resource resource = 3;
}
123 changes: 123 additions & 0 deletions cmd/talosctl/cmd/talos/get.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

package talos

import (
"context"
"fmt"
"io"
"os"

"github.com/spf13/cobra"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"

"github.com/talos-systems/talos/cmd/talosctl/cmd/talos/output"
"github.com/talos-systems/talos/pkg/machinery/client"
)

var getCmdFlags struct {
namespace string

output string
}

// getCmd represents the get (resources) command.
var getCmd = &cobra.Command{
Use: "get <type> [<id>]",
Aliases: []string{"g"},
Short: "Get a specific resource or list of resources.",
Long: ``,
Args: cobra.RangeArgs(1, 2),
RunE: func(cmd *cobra.Command, args []string) error {
return WithClient(func(ctx context.Context, c *client.Client) error {
out, err := output.NewWriter(getCmdFlags.output)
if err != nil {
return err
}

resourceType := args[0]

var resourceID string

if len(args) == 2 {
resourceID = args[1]
}

defer out.Flush() //nolint: errcheck

var headerWritten bool

if resourceID != "" {
resp, err := c.Resources.Get(ctx, getCmdFlags.namespace, resourceType, resourceID)

for _, msg := range resp {
if msg.Definition != nil && !headerWritten {
if e := out.WriteHeader(msg.Definition); e != nil {
return e
}

headerWritten = true
}

if msg.Resource != nil {
if e := out.WriteResource(msg.Metadata.GetHostname(), msg.Resource); e != nil {
return e
}
}

}

if err != nil {
return err
}
} else {
listClient, err := c.Resources.List(ctx, getCmdFlags.namespace, resourceType)
if err != nil {
return err
}

for {
msg, err := listClient.Recv()
if err != nil {
if err == io.EOF || status.Code(err) == codes.Canceled {
return nil
}

return err
}

if msg.Metadata.GetError() != "" {
fmt.Fprintf(os.Stderr, "%s: %s\n", msg.Metadata.GetHostname(), msg.Metadata.GetError())

continue
}

if msg.Definition != nil && !headerWritten {
if e := out.WriteHeader(msg.Definition); e != nil {
return e
}

headerWritten = true
}

if msg.Resource != nil {
if err := out.WriteResource(msg.Metadata.GetHostname(), msg.Resource); err != nil {
return err
}
}
}
}

return nil
})
},
}

func init() {
getCmd.Flags().StringVar(&getCmdFlags.namespace, "namespace", "", "resource namespace (default is to use default namespace per resource)")
getCmd.Flags().StringVarP(&getCmdFlags.output, "output", "o", "table", "output mode (table, yaml)")
addCommand(getCmd)
}
31 changes: 31 additions & 0 deletions cmd/talosctl/cmd/talos/output/output.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

// Package output provides writers in different formats.
package output

import (
"fmt"

"github.com/talos-systems/os-runtime/pkg/resource"
)

// Writer interface.
type Writer interface {
WriteHeader(definition resource.Resource) error
WriteResource(node string, r resource.Resource) error
Flush() error
}

// NewWriter builds writer from type.
func NewWriter(format string) (Writer, error) {
switch format {
case "table":
return NewTable(), nil
case "yaml":
return NewYAML(), nil
default:
return nil, fmt.Errorf("output format %q is not supported", format)
}
}
45 changes: 45 additions & 0 deletions cmd/talosctl/cmd/talos/output/table.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

package output

import (
"fmt"
"os"
"text/tabwriter"

"github.com/talos-systems/os-runtime/pkg/resource"
)

// Table outputs resources in Table view.
type Table struct {
w tabwriter.Writer
}

// NewTable initializes table resource output.
func NewTable() *Table {
output := &Table{}
output.w.Init(os.Stdout, 0, 0, 3, ' ', 0)

return output
}

// WriteHeader implements output.Writer interface.
func (table *Table) WriteHeader(definition resource.Resource) error {
_, err := fmt.Fprintln(&table.w, "NODE\tNAMESPACE\tTYPE\tID")

return err
}

// WriteResource implements output.Writer interface.
func (table *Table) WriteResource(node string, r resource.Resource) error {
_, err := fmt.Fprintf(&table.w, "%s\t%s\t%s\t%s\n", node, r.Metadata().Namespace(), r.Metadata().Type(), r.Metadata().ID())

return err
}

// Flush implements output.Writer interface.
func (table *Table) Flush() error {
return table.w.Flush()
}
51 changes: 51 additions & 0 deletions cmd/talosctl/cmd/talos/output/yaml.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

package output

import (
"fmt"
"os"

"github.com/talos-systems/os-runtime/pkg/resource"
"gopkg.in/yaml.v3"
)

// YAML outputs resources in YAML format.
type YAML struct {
needDashes bool
}

// NewYAML initializes YAML resource output.
func NewYAML() *YAML {
return &YAML{}
}

// WriteHeader implements output.Writer interface.
func (y *YAML) WriteHeader(definition resource.Resource) error {
return nil
}

// WriteResource implements output.Writer interface.
func (y *YAML) WriteResource(node string, r resource.Resource) error {
out, err := resource.MarshalYAML(r)
if err != nil {
return err
}

if y.needDashes {
fmt.Fprintln(os.Stdout, "---")
}

y.needDashes = true

fmt.Fprintf(os.Stdout, "node: %s\n", node)

return yaml.NewEncoder(os.Stdout).Encode(out)
}

// Flush implements output.Writer interface.
func (y *YAML) Flush() error {
return nil
}
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,11 @@ require (
github.com/talos-systems/go-blockdevice v0.1.1-0.20201218174450-f2728a581972
github.com/talos-systems/go-loadbalancer v0.1.0
github.com/talos-systems/go-procfs v0.0.0-20210108152626-8cbc42d3dc24
github.com/talos-systems/go-retry v0.1.1-0.20201113203059-8c63d290a688
github.com/talos-systems/go-retry v0.2.0
github.com/talos-systems/go-smbios v0.0.0-20200807005123-80196199691e
github.com/talos-systems/grpc-proxy v0.2.0
github.com/talos-systems/net v0.2.0
github.com/talos-systems/os-runtime v0.0.0-20210119124441-98acf0d2d332
github.com/talos-systems/talos/pkg/machinery v0.0.0-20200818212414-6a7cc0264819
github.com/u-root/u-root v7.0.0+incompatible
github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae // indirect
Expand Down
Loading

0 comments on commit 11863dd

Please sign in to comment.