Skip to content

Commit

Permalink
Merge pull request #7997 from andfasano/agent-day2-clusterinfo-asset
Browse files Browse the repository at this point in the history
AGENT-853: Add ClusterInfo asset
  • Loading branch information
openshift-merge-bot[bot] authored Mar 1, 2024
2 parents 62209ca + d56bab4 commit 9552fc8
Show file tree
Hide file tree
Showing 7 changed files with 344 additions and 3 deletions.
7 changes: 6 additions & 1 deletion cmd/node-joiner/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,11 @@ func main() {
Use: "add-nodes",
Short: "Generates an ISO that could be used to boot the configured nodes to let them join an existing cluster",
RunE: func(cmd *cobra.Command, args []string) error {
return nodejoiner.NewAddNodesCommand(wd)
kubeConfig, err := cmd.Flags().GetString("kubeconfig")
if err != nil {
return err
}
return nodejoiner.NewAddNodesCommand(wd, kubeConfig)
},
}

Expand All @@ -34,6 +38,7 @@ func main() {
rootCmd := &cobra.Command{
Use: "node-joiner",
}
rootCmd.PersistentFlags().String("kubeconfig", "", "Path to the kubeconfig file.")

rootCmd.AddCommand(nodesAddCmd)
rootCmd.AddCommand(nodesMonitorCmd)
Expand Down
80 changes: 80 additions & 0 deletions pkg/asset/agent/joiner/addnodesconfig.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package joiner

import (
"encoding/json"
"fmt"
"os"
"path/filepath"

"github.com/openshift/installer/pkg/asset"
)

const (
addNodesParamsFile = ".addnodesparams"
)

// AddNodesConfig is used to store the current configuration
// for the command.
type AddNodesConfig struct {
File *asset.File
Params Params
}

// Params is used to store the command line parameters.
type Params struct {
Kubeconfig string `json:"kubeconfig,omitempty"`
}

// Save stores the current parameters on disk.
func (p *Params) Save(assetsDir string) error {
data, err := json.Marshal(p)
if err != nil {
return err
}

fileName := filepath.Join(assetsDir, addNodesParamsFile)
return os.WriteFile(fileName, data, 0o600)
}

// Name returns the human-friendly name of the asset.
func (*AddNodesConfig) Name() string {
return "AddNodes Config"
}

// Dependencies returns all of the dependencies directly needed to generate
// the asset.
func (*AddNodesConfig) Dependencies() []asset.Asset {
return []asset.Asset{}
}

// Generate it's empty for this asset, always loaded from disk.
func (*AddNodesConfig) Generate(dependencies asset.Parents) error {
return nil
}

// Files returns the files generated by the asset.
func (a *AddNodesConfig) Files() []*asset.File {
if a.File != nil {
return []*asset.File{a.File}
}
return []*asset.File{}
}

// Load returns agent config asset from the disk.
func (a *AddNodesConfig) Load(f asset.FileFetcher) (bool, error) {
file, err := f.FetchByName(addNodesParamsFile)
if err != nil {
if os.IsNotExist(err) {
return false, nil
}
return false, fmt.Errorf("failed to load %s file: %w", addNodesParamsFile, err)
}

params := &Params{}
if err := json.Unmarshal(file.Data, params); err != nil {
return false, fmt.Errorf("failed to unmarshal %s: %w", addNodesParamsFile, err)
}

a.Params = *params
return true, nil
}
135 changes: 135 additions & 0 deletions pkg/asset/agent/joiner/clusterinfo.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
package joiner

import (
"context"
"net/url"

metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"

configclient "github.com/openshift/client-go/config/clientset/versioned"
"github.com/openshift/installer/pkg/asset"
"github.com/openshift/installer/pkg/asset/agent/workflow"
)

// ClusterInfo it's an asset used to retrieve config info
// from an already existing cluster.
type ClusterInfo struct {
ClusterID string
APIDNSName string
PullSecret string
}

var _ asset.WritableAsset = (*ClusterInfo)(nil)

// Name returns the human-friendly name of the asset.
func (ci *ClusterInfo) Name() string {
return "Agent Installer ClusterInfo"
}

// Dependencies returns all of the dependencies directly needed to generate
// the asset.
func (*ClusterInfo) Dependencies() []asset.Asset {
return []asset.Asset{
&workflow.AgentWorkflow{},
&AddNodesConfig{},
}
}

// Generate generates the ClusterInfo.
func (ci *ClusterInfo) Generate(dependencies asset.Parents) error {
agentWorkflow := &workflow.AgentWorkflow{}
addNodesConfig := &AddNodesConfig{}
dependencies.Get(agentWorkflow, addNodesConfig)

if agentWorkflow.Workflow != workflow.AgentWorkflowTypeAddNodes {
return nil
}

config, err := ci.getRestConfig(addNodesConfig.Params.Kubeconfig)
if err != nil {
return err
}

err = ci.retrieveClusterID(config)
if err != nil {
return err
}

err = ci.retrieveAPIDNSName(config)
if err != nil {
return err
}

err = ci.retrievePullSecret(config)
if err != nil {
return err
}

return nil
}

func (ci *ClusterInfo) getRestConfig(kubeconfig string) (*rest.Config, error) {
var err error
var config *rest.Config

if kubeconfig != "" {
config, err = clientcmd.BuildConfigFromFlags("", kubeconfig)
} else {
config, err = rest.InClusterConfig()
}

return config, err
}

func (ci *ClusterInfo) retrieveClusterID(config *rest.Config) error {
clientset, err := configclient.NewForConfig(config)
if err != nil {
return err
}

cv, err := clientset.ConfigV1().ClusterVersions().Get(context.Background(), "version", metav1.GetOptions{})
if err != nil {
return err
}
ci.ClusterID = string(cv.Spec.ClusterID)

return nil
}

func (ci *ClusterInfo) retrieveAPIDNSName(config *rest.Config) error {
parsedURL, err := url.Parse(config.Host)
if err != nil {
return err
}

ci.APIDNSName = parsedURL.Hostname()
return nil
}

func (ci *ClusterInfo) retrievePullSecret(config *rest.Config) error {
clientset, err := kubernetes.NewForConfig(config)
if err != nil {
return err
}

pullSecret, err := clientset.CoreV1().Secrets("openshift-config").Get(context.Background(), "pull-secret", metav1.GetOptions{})
if err != nil {
return err
}
ci.PullSecret = string(pullSecret.Data[".dockerconfigjson"])

return nil
}

// Files returns the files generated by the asset.
func (*ClusterInfo) Files() []*asset.File {
return []*asset.File{}
}

// Load returns agent config asset from the disk.
func (*ClusterInfo) Load(f asset.FileFetcher) (bool, error) {
return false, nil
}
65 changes: 65 additions & 0 deletions pkg/asset/agent/workflow/agentworkflow.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package workflow

import (
"fmt"
"os"

"github.com/openshift/installer/pkg/asset"
)

// AgentWorkflow allows other assets to check
// which is the workflow currently active.
type AgentWorkflow struct {
File *asset.File
Workflow AgentWorkflowType
}

var _ asset.WritableAsset = (*AgentWorkflow)(nil)

// Name returns a human friendly name for the asset.
func (*AgentWorkflow) Name() string {
return "Agent Workflow"
}

// Dependencies returns all of the dependencies directly needed to generate
// the asset.
func (*AgentWorkflow) Dependencies() []asset.Asset {
return []asset.Asset{}
}

// Generate generates the AgentWorkflow asset.
func (a *AgentWorkflow) Generate(dependencies asset.Parents) error {
// Set install workflow as a default
a.Workflow = AgentWorkflowTypeInstall
a.File = &asset.File{
Filename: agentWorkflowFilename,
Data: []byte(a.Workflow),
}

return nil
}

// Files returns the files generated by the asset.
func (a *AgentWorkflow) Files() []*asset.File {
if a.File != nil {
return []*asset.File{a.File}
}
return []*asset.File{}
}

// Load returns the asset from disk.
func (a *AgentWorkflow) Load(f asset.FileFetcher) (bool, error) {
file, err := f.FetchByName(agentWorkflowFilename)
if err != nil {
if os.IsNotExist(err) {
return false, nil
}
return false, fmt.Errorf("failed to load %s file: %w", agentWorkflowFilename, err)
}

// Get the current workflow
a.Workflow = AgentWorkflowType(file.Data)
a.File = file

return true, nil
}
27 changes: 27 additions & 0 deletions pkg/asset/agent/workflow/agentworkflowaddnodes.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package workflow

import "github.com/openshift/installer/pkg/asset"

// AgentWorkflowAddNodes is meant just to define
// the add nodes workflow.
type AgentWorkflowAddNodes struct {
AgentWorkflow
}

var _ asset.WritableAsset = (*AgentWorkflowAddNodes)(nil)

// Name returns a human friendly name for the asset.
func (*AgentWorkflowAddNodes) Name() string {
return "Agent Workflow Add Nodes"
}

// Generate generates the AgentWorkflow asset.
func (a *AgentWorkflowAddNodes) Generate(dependencies asset.Parents) error {
a.Workflow = AgentWorkflowTypeAddNodes
a.File = &asset.File{
Filename: agentWorkflowFilename,
Data: []byte(a.Workflow),
}

return nil
}
14 changes: 14 additions & 0 deletions pkg/asset/agent/workflow/commons.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package workflow

// AgentWorkflowType defines the supported
// agent workflows.
type AgentWorkflowType string

const (
// AgentWorkflowTypeInstall identifies the install workflow.
AgentWorkflowTypeInstall AgentWorkflowType = "install"
// AgentWorkflowTypeAddNodes identifies the add nodes workflow.
AgentWorkflowTypeAddNodes AgentWorkflowType = "addnodes"

agentWorkflowFilename = ".agentworkflow"
)
19 changes: 17 additions & 2 deletions pkg/nodejoiner/addnodes.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,26 @@ package nodejoiner

import (
"github.com/openshift/installer/pkg/asset"
"github.com/openshift/installer/pkg/asset/agent/joiner"
"github.com/openshift/installer/pkg/asset/agent/workflow"
"github.com/openshift/installer/pkg/asset/store"
)

// NewAddNodesCommand creates a new command for add nodes.
func NewAddNodesCommand(directory string) error {
func NewAddNodesCommand(directory string, kubeConfig string) error {
// Store the current parameters into the assets folder, so
// that they could be retrieved later by the assets
params := joiner.Params{
Kubeconfig: kubeConfig,
}
err := params.Save(directory)
if err != nil {
return err
}

fetcher := store.NewAssetsFetcher(directory)
return fetcher.FetchAndPersist([]asset.WritableAsset{})
return fetcher.FetchAndPersist([]asset.WritableAsset{
&workflow.AgentWorkflowAddNodes{},
// To be completed
})
}

0 comments on commit 9552fc8

Please sign in to comment.