@@ -14,10 +14,14 @@ import (
1414 "time"
1515
1616 "github.com/google/uuid"
17+ "github.com/k0sproject/dig"
18+ "github.com/k0sproject/k0sctl/pkg/apis/k0sctl.k0sproject.io/v1beta1/cluster"
1719 "github.com/sirupsen/logrus"
1820 "github.com/urfave/cli/v2"
21+ "gopkg.in/yaml.v2"
1922
2023 "github.com/replicatedhq/embedded-cluster/pkg/addons"
24+ "github.com/replicatedhq/embedded-cluster/pkg/config"
2125 "github.com/replicatedhq/embedded-cluster/pkg/defaults"
2226 "github.com/replicatedhq/embedded-cluster/pkg/goods"
2327 "github.com/replicatedhq/embedded-cluster/pkg/metrics"
@@ -26,12 +30,43 @@ import (
2630 "github.com/replicatedhq/embedded-cluster/pkg/prompts"
2731)
2832
29- // JoinCommandResponse is the response from the kots api we use to fetch the k0s join
30- // token. It returns the actual command we need to run and also the cluster ID.
33+ // JoinCommandResponse is the response from the kots api we use to fetch the k0s join token.
3134type JoinCommandResponse struct {
32- K0sJoinCommand string `json:"k0sJoinCommand"`
33- K0sToken string `json:"k0sToken"`
34- ClusterID uuid.UUID `json:"clusterID"`
35+ K0sJoinCommand string `json:"k0sJoinCommand"`
36+ K0sToken string `json:"k0sToken"`
37+ ClusterID uuid.UUID `json:"clusterID"`
38+ K0sUnsupportedOverrides string `json:"k0sUnsupportedOverrides"`
39+ EndUserK0sConfigOverrides string `json:"endUserK0sConfigOverrides"`
40+ }
41+
42+ // extractK0sConfigOverridePatch parses the provided override and returns a dig.Mapping that
43+ // can be then applied on top a k0s configuration file to set both `api` and `storage` spec
44+ // fields. All other fields in the override are ignored.
45+ func (j JoinCommandResponse ) extractK0sConfigOverridePatch (data []byte ) (dig.Mapping , error ) {
46+ config := dig.Mapping {}
47+ if err := yaml .Unmarshal (data , & config ); err != nil {
48+ return nil , fmt .Errorf ("unable to unmarshal embedded config: %w" , err )
49+ }
50+ result := dig.Mapping {}
51+ if api := config .DigMapping ("config" , "spec" , "api" ); len (api ) > 0 {
52+ result .DigMapping ("config" , "spec" )["api" ] = api
53+ }
54+ if storage := config .DigMapping ("config" , "spec" , "storage" ); len (storage ) > 0 {
55+ result .DigMapping ("config" , "spec" )["storage" ] = storage
56+ }
57+ return result , nil
58+ }
59+
60+ // EndUserOverrides returns a dig.Mapping that can be applied on top of a k0s configuration.
61+ // This patch is assembled based on the EndUserK0sConfigOverrides field.
62+ func (j JoinCommandResponse ) EndUserOverrides () (dig.Mapping , error ) {
63+ return j .extractK0sConfigOverridePatch ([]byte (j .EndUserK0sConfigOverrides ))
64+ }
65+
66+ // EmbeddedOverrides returns a dig.Mapping that can be applied on top of a k0s configuration.
67+ // This patch is assembled based on the K0sUnsupportedOverrides field.
68+ func (j JoinCommandResponse ) EmbeddedOverrides () (dig.Mapping , error ) {
69+ return j .extractK0sConfigOverridePatch ([]byte (j .K0sUnsupportedOverrides ))
3570}
3671
3772// getJoinToken issues a request to the kots api to get the actual join command
@@ -108,6 +143,12 @@ var joinCommand = &cli.Command{
108143 metrics .ReportJoinFailed (c .Context , jcmd .ClusterID , err )
109144 return err
110145 }
146+ loading .Infof ("Applying configuration overrides" )
147+ if err := applyJoinConfigurationOverrides (c , jcmd ); err != nil {
148+ err := fmt .Errorf ("unable to apply configuration overrides: %w" , err )
149+ metrics .ReportJoinFailed (c .Context , jcmd .ClusterID , err )
150+ return err
151+ }
111152 loading .Infof ("Creating systemd unit file" )
112153 if err := createSystemdUnitFile (jcmd .K0sJoinCommand ); err != nil {
113154 err := fmt .Errorf ("unable to create systemd unit file: %w" , err )
@@ -133,6 +174,75 @@ var joinCommand = &cli.Command{
133174 },
134175}
135176
177+ // applyJoinConfigurationOverrides applies both config overrides received from the kots api.
178+ // Applies first the EmbeddedOverrides and then the EndUserOverrides.
179+ func applyJoinConfigurationOverrides (c * cli.Context , jcmd * JoinCommandResponse ) error {
180+ patch , err := jcmd .EmbeddedOverrides ()
181+ if err != nil {
182+ return fmt .Errorf ("unable to get embedded overrides: %w" , err )
183+ }
184+ if len (patch ) > 0 {
185+ if data , err := yaml .Marshal (patch ); err != nil {
186+ return fmt .Errorf ("unable to marshal embedded overrides: %w" , err )
187+ } else if err := patchK0sConfig ("/etc/k0s/k0s.yaml" , string (data )); err != nil {
188+ return fmt .Errorf ("unable to patch config with embedded data: %w" , err )
189+ }
190+ }
191+ if patch , err = jcmd .EndUserOverrides (); err != nil {
192+ return fmt .Errorf ("unable to get embedded overrides: %w" , err )
193+ } else if len (patch ) == 0 {
194+ return nil
195+ }
196+ if data , err := yaml .Marshal (patch ); err != nil {
197+ return fmt .Errorf ("unable to marshal embedded overrides: %w" , err )
198+ } else if err := patchK0sConfig ("/etc/k0s/k0s.yaml" , string (data )); err != nil {
199+ return fmt .Errorf ("unable to patch config with embedded data: %w" , err )
200+ }
201+ return nil
202+ }
203+
204+ // patchK0sConfig patches the created k0s config with the unsupported overrides passed in.
205+ func patchK0sConfig (path string , patch string ) error {
206+ if len (patch ) == 0 {
207+ return nil
208+ }
209+ finalcfg := dig.Mapping {
210+ "apiVersion" : "k0s.k0sproject.io/v1beta1" ,
211+ "kind" : "ClusterConfig" ,
212+ "metadata" : dig.Mapping {"name" : defaults .BinaryName ()},
213+ }
214+ if _ , err := os .Stat (path ); err == nil {
215+ data , err := os .ReadFile (path )
216+ if err != nil {
217+ return fmt .Errorf ("unable to read node config: %w" , err )
218+ }
219+ finalcfg = dig.Mapping {}
220+ if err := yaml .Unmarshal (data , & finalcfg ); err != nil {
221+ return fmt .Errorf ("unable to unmarshal node config: %w" , err )
222+ }
223+ }
224+ k0sconfig := cluster.K0s {Config : finalcfg .Dup ()}
225+ result , err := config .PatchK0sConfig (& k0sconfig , patch )
226+ if err != nil {
227+ return fmt .Errorf ("unable to patch node config: %w" , err )
228+ }
229+ if len (result .Config .DigMapping ("spec" , "api" )) > 0 {
230+ finalcfg .DigMapping ("spec" )["api" ] = result .Config .DigMapping ("spec" , "api" )
231+ }
232+ if len (result .Config .DigMapping ("spec" , "storage" )) > 0 {
233+ finalcfg .DigMapping ("spec" )["storage" ] = result .Config .DigMapping ("spec" , "storage" )
234+ }
235+ out , err := os .OpenFile (path , os .O_CREATE | os .O_WRONLY | os .O_TRUNC , 0600 )
236+ if err != nil {
237+ return fmt .Errorf ("unable to open node config file for writing: %w" , err )
238+ }
239+ defer out .Close ()
240+ if err := yaml .NewEncoder (out ).Encode (finalcfg ); err != nil {
241+ return fmt .Errorf ("unable to write node config: %w" , err )
242+ }
243+ return nil
244+ }
245+
136246// saveTokenToDisk saves the provided token in "/etc/k0s/join-token".
137247func saveTokenToDisk (token string ) error {
138248 if err := os .MkdirAll ("/etc/k0s" , 0755 ); err != nil {
0 commit comments