From e2c458670639d5b2030ddd3ce3cc7e725e807f50 Mon Sep 17 00:00:00 2001 From: Christian Richter Date: Fri, 26 Jul 2024 07:49:37 +0200 Subject: [PATCH 1/5] add diff mode to init Signed-off-by: Christian Richter --- ocis/pkg/command/init.go | 8 +- ocis/pkg/init/init.go | 412 ++++++++++++++------------------------- ocis/pkg/init/structs.go | 213 ++++++++++++++++++++ 3 files changed, 363 insertions(+), 270 deletions(-) create mode 100644 ocis/pkg/init/structs.go diff --git a/ocis/pkg/command/init.go b/ocis/pkg/command/init.go index fff17382d0b..bafd54f856e 100644 --- a/ocis/pkg/command/init.go +++ b/ocis/pkg/command/init.go @@ -26,6 +26,12 @@ func InitCommand(cfg *config.Config) *cli.Command { Value: "ask", Usage: "Allow insecure oCIS config", }, + &cli.BoolFlag{ + Name: "diff", + Aliases: []string{"d"}, + Usage: "Show the difference between the current config and the new one", + Value: false, + }, &cli.BoolFlag{ Name: "force-overwrite", Aliases: []string{"f"}, @@ -57,7 +63,7 @@ func InitCommand(cfg *config.Config) *cli.Command { } else if insecureFlag == strings.ToLower("true") || insecureFlag == strings.ToLower("yes") || insecureFlag == strings.ToLower("y") { insecure = true } - err := ocisinit.CreateConfig(insecure, c.Bool("force-overwrite"), c.String("config-path"), c.String("admin-password")) + err := ocisinit.CreateConfig(insecure, c.Bool("force-overwrite"), c.Bool("diff"), c.String("config-path"), c.String("admin-password")) if err != nil { log.Fatalf("Could not create config: %s", err) } diff --git a/ocis/pkg/init/init.go b/ocis/pkg/init/init.go index 14c88214dc3..8391e9b5831 100644 --- a/ocis/pkg/init/init.go +++ b/ocis/pkg/init/init.go @@ -2,13 +2,14 @@ package init import ( "fmt" + "github.com/gofrs/uuid" "io" "log" "os" + "os/exec" "path" "time" - "github.com/gofrs/uuid" "github.com/owncloud/ocis/v2/ocis-pkg/generators" "gopkg.in/yaml.v2" ) @@ -23,216 +24,6 @@ var ( _insecureEvents = Events{TLSInsecure: true} ) -type TokenManager struct { - JWTSecret string `yaml:"jwt_secret"` -} - -type InsecureService struct { - Insecure bool -} - -type ProxyService struct { - OIDC InsecureProxyOIDC `yaml:"oidc"` - InsecureBackends bool `yaml:"insecure_backends"` - ServiceAccount ServiceAccount `yaml:"service_account"` -} - -type InsecureProxyOIDC struct { - Insecure bool `yaml:"insecure"` -} - -type LdapSettings struct { - BindPassword string `yaml:"bind_password"` -} -type LdapBasedService struct { - Ldap LdapSettings -} - -type Events struct { - TLSInsecure bool `yaml:"tls_insecure"` -} -type GraphApplication struct { - ID string `yaml:"id"` -} - -type GraphService struct { - Application GraphApplication - Events Events - Spaces InsecureService - Identity LdapBasedService - ServiceAccount ServiceAccount `yaml:"service_account"` -} - -type ServiceUserPasswordsSettings struct { - AdminPassword string `yaml:"admin_password"` - IdmPassword string `yaml:"idm_password"` - RevaPassword string `yaml:"reva_password"` - IdpPassword string `yaml:"idp_password"` -} -type IdmService struct { - ServiceUserPasswords ServiceUserPasswordsSettings `yaml:"service_user_passwords"` -} - -type SettingsService struct { - ServiceAccountIDs []string `yaml:"service_account_ids"` -} - -type FrontendService struct { - AppHandler InsecureService `yaml:"app_handler"` - Archiver InsecureService - ServiceAccount ServiceAccount `yaml:"service_account"` -} - -type OcmService struct { - ServiceAccount ServiceAccount `yaml:"service_account"` -} - -type AuthbasicService struct { - AuthProviders LdapBasedService `yaml:"auth_providers"` -} - -type AuthProviderSettings struct { - Oidc InsecureService -} -type AuthbearerService struct { - AuthProviders AuthProviderSettings `yaml:"auth_providers"` -} - -type UsersAndGroupsService struct { - Drivers LdapBasedService -} - -type ThumbnailSettings struct { - TransferSecret string `yaml:"transfer_secret"` - WebdavAllowInsecure bool `yaml:"webdav_allow_insecure"` - Cs3AllowInsecure bool `yaml:"cs3_allow_insecure"` -} - -type ThumbnailService struct { - Thumbnail ThumbnailSettings -} - -type Search struct { - Events Events - ServiceAccount ServiceAccount `yaml:"service_account"` -} - -type Audit struct { - Events Events -} - -type Sharing struct { - Events Events -} - -type StorageUsers struct { - Events Events - MountID string `yaml:"mount_id"` - ServiceAccount ServiceAccount `yaml:"service_account"` -} - -type Gateway struct { - StorageRegistry StorageRegistry `yaml:"storage_registry"` -} - -type StorageRegistry struct { - StorageUsersMountID string `yaml:"storage_users_mount_id"` -} - -type Notifications struct { - Notifications struct{ Events Events } // The notifications config has a field called notifications - ServiceAccount ServiceAccount `yaml:"service_account"` -} - -type Userlog struct { - ServiceAccount ServiceAccount `yaml:"service_account"` -} - -type AuthService struct { - ServiceAccount ServiceAccount `yaml:"service_account"` -} - -type Clientlog struct { - ServiceAccount ServiceAccount `yaml:"service_account"` -} - -type WopiApp struct { - Secret string `yaml:"secret"` -} - -type App struct { - Insecure bool `yaml:"insecure"` -} - -type Collaboration struct { - WopiApp WopiApp `yaml:"wopi"` - App App `yaml:"app"` -} - -type Nats struct { - // The nats config has a field called nats - Nats struct { - TLSSkipVerifyClientCert bool `yaml:"tls_skip_verify_client_cert"` - } -} - -// Activitylog is the configuration for the activitylog service -type Activitylog struct { - ServiceAccount ServiceAccount `yaml:"service_account"` -} - -// ServiceAccount is the configuration for the used service account -type ServiceAccount struct { - ServiceAccountID string `yaml:"service_account_id"` - ServiceAccountSecret string `yaml:"service_account_secret"` -} - -// TODO: use the oCIS config struct instead of this custom struct -// We can't use it right now, because it would need "omitempty" on -// all elements, in order to produce a slim config file with `ocis init`. -// We can't just add these "omitempty" tags, since we want to generate -// full example configuration files with that struct, too. -// Proposed solution to get rid of this temporary solution: -// - use the oCIS config struct -// - set the needed values like below -// - marshal it to yaml -// - unmarshal it into yaml.Node -// - recurse through the nodes and delete empty / default ones -// - marshal it to yaml -type OcisConfig struct { - TokenManager TokenManager `yaml:"token_manager"` - MachineAuthAPIKey string `yaml:"machine_auth_api_key"` - SystemUserAPIKey string `yaml:"system_user_api_key"` - TransferSecret string `yaml:"transfer_secret"` - SystemUserID string `yaml:"system_user_id"` - AdminUserID string `yaml:"admin_user_id"` - Graph GraphService - Idp LdapBasedService - Idm IdmService - Collaboration Collaboration - Proxy ProxyService - Frontend FrontendService - AuthBasic AuthbasicService `yaml:"auth_basic"` - AuthBearer AuthbearerService `yaml:"auth_bearer"` - Users UsersAndGroupsService - Groups UsersAndGroupsService - Ocdav InsecureService - Ocm OcmService - Thumbnails ThumbnailService - Search Search - Audit Audit - Settings SettingsService `yaml:"settings"` - Sharing Sharing - StorageUsers StorageUsers `yaml:"storage_users"` - Notifications Notifications - Nats Nats - Gateway Gateway - Userlog Userlog - AuthService AuthService `yaml:"auth_service"` - Clientlog Clientlog - Activitylog Activitylog -} - func checkConfigPath(configPath string) error { targetPath := path.Join(configPath, configFilename) if _, err := os.Stat(targetPath); err == nil { @@ -241,6 +32,14 @@ func checkConfigPath(configPath string) error { return nil } +func configExists(configPath string) bool { + targetPath := path.Join(configPath, configFilename) + if _, err := os.Stat(targetPath); err == nil { + return true + } + return false +} + func backupOcisConfigFile(configPath string) (string, error) { sourceConfig := path.Join(configPath, configFilename) targetBackupConfig := path.Join(configPath, configFilename+"."+time.Now().Format("2006-01-02-15-04-05")+".backup") @@ -262,9 +61,25 @@ func backupOcisConfigFile(configPath string) (string, error) { } // CreateConfig creates a config file with random passwords at configPath -func CreateConfig(insecure, forceOverwrite bool, configPath, adminPassword string) error { +func CreateConfig(insecure, forceOverwrite, diff bool, configPath, adminPassword string) error { + if diff { + if forceOverwrite { + return fmt.Errorf("diff and force-overwrite flags are mutually exclusive") + } + if adminPassword != "" { + return fmt.Errorf("diff and admin-password flags are mutually exclusive") + } + } + + if configExists(configPath) { + if !forceOverwrite && !diff { + return fmt.Errorf("config file already exists, use --force-overwrite to overwrite or --diff to show diff") + } + } + err := checkConfigPath(configPath) - if err != nil && !forceOverwrite { + if err != nil && (!forceOverwrite && !diff) { + fmt.Println("off") return err } targetBackupConfig := "" @@ -279,59 +94,96 @@ func CreateConfig(insecure, forceOverwrite bool, configPath, adminPassword strin return err } - systemUserID := uuid.Must(uuid.NewV4()).String() - adminUserID := uuid.Must(uuid.NewV4()).String() - graphApplicationID := uuid.Must(uuid.NewV4()).String() - storageUsersMountID := uuid.Must(uuid.NewV4()).String() - serviceAccountID := uuid.Must(uuid.NewV4()).String() - - idmServicePassword, err := generators.GenerateRandomPassword(passwordLength) - if err != nil { - return fmt.Errorf("could not generate random password for idm: %s", err) - } - idpServicePassword, err := generators.GenerateRandomPassword(passwordLength) - if err != nil { - return fmt.Errorf("could not generate random password for idp: %s", err) - } - ocisAdminServicePassword := adminPassword - if ocisAdminServicePassword == "" { - ocisAdminServicePassword, err = generators.GenerateRandomPassword(passwordLength) + // Load old config + var oldCfg OcisConfig + if diff { + fp, err := os.ReadFile(path.Join(configPath, configFilename)) if err != nil { - return fmt.Errorf("could not generate random password for ocis admin: %s", err) + return err } + err = yaml.Unmarshal(fp, &oldCfg) } - revaServicePassword, err := generators.GenerateRandomPassword(passwordLength) - if err != nil { - return fmt.Errorf("could not generate random password for reva: %s", err) - } - tokenManagerJwtSecret, err := generators.GenerateRandomPassword(passwordLength) - if err != nil { - return fmt.Errorf("could not generate random password for tokenmanager: %s", err) - } - collaborationWOPISecret, err := generators.GenerateRandomPassword(passwordLength) - if err != nil { - return fmt.Errorf("could not generate random wopi secret for collaboration service: %s", err) - } - machineAuthAPIKey, err := generators.GenerateRandomPassword(passwordLength) - if err != nil { - return fmt.Errorf("could not generate random password for machineauthsecret: %s", err) - } - systemUserAPIKey, err := generators.GenerateRandomPassword(passwordLength) - if err != nil { - return fmt.Errorf("could not generate random system user API key: %s", err) - } - revaTransferSecret, err := generators.GenerateRandomPassword(passwordLength) - if err != nil { - return fmt.Errorf("could not generate random password for revaTransferSecret: %s", err) - } - thumbnailsTransferSecret, err := generators.GenerateRandomPassword(passwordLength) - if err != nil { - return fmt.Errorf("could not generate random password for thumbnailsTransferSecret: %s", err) - } - serviceAccountSecret, err := generators.GenerateRandomPassword(passwordLength) - if err != nil { - return fmt.Errorf("could not generate random password for thumbnailsTransferSecret: %s", err) + var ( + systemUserID, adminUserID, graphApplicationID, storageUsersMountID, serviceAccountID string + idmServicePassword, idpServicePassword, ocisAdminServicePassword, revaServicePassword string + tokenManagerJwtSecret, collaborationWOPISecret, machineAuthAPIKey, systemUserAPIKey string + revaTransferSecret, thumbnailsTransferSecret, serviceAccountSecret string + ) + + if diff { + systemUserID = oldCfg.SystemUserID + adminUserID = oldCfg.AdminUserID + graphApplicationID = oldCfg.Graph.Application.ID + storageUsersMountID = oldCfg.Gateway.StorageRegistry.StorageUsersMountID + serviceAccountID = oldCfg.Graph.ServiceAccount.ServiceAccountID + + idmServicePassword = oldCfg.Idm.ServiceUserPasswords.IdmPassword + idpServicePassword = oldCfg.Idm.ServiceUserPasswords.IdpPassword + ocisAdminServicePassword = oldCfg.Idm.ServiceUserPasswords.AdminPassword + revaServicePassword = oldCfg.Idm.ServiceUserPasswords.RevaPassword + tokenManagerJwtSecret = oldCfg.TokenManager.JWTSecret + collaborationWOPISecret = oldCfg.Collaboration.WopiApp.Secret + machineAuthAPIKey = oldCfg.MachineAuthAPIKey + systemUserAPIKey = oldCfg.SystemUserAPIKey + revaTransferSecret = oldCfg.TransferSecret + thumbnailsTransferSecret = oldCfg.Thumbnails.Thumbnail.TransferSecret + serviceAccountSecret = oldCfg.Graph.ServiceAccount.ServiceAccountSecret + } else { + systemUserID = uuid.Must(uuid.NewV4()).String() + adminUserID = uuid.Must(uuid.NewV4()).String() + graphApplicationID = uuid.Must(uuid.NewV4()).String() + storageUsersMountID = uuid.Must(uuid.NewV4()).String() + serviceAccountID = uuid.Must(uuid.NewV4()).String() + + idmServicePassword, err = generators.GenerateRandomPassword(passwordLength) + if err != nil { + return fmt.Errorf("could not generate random password for idm: %s", err) + } + idpServicePassword, err = generators.GenerateRandomPassword(passwordLength) + if err != nil { + return fmt.Errorf("could not generate random password for idp: %s", err) + } + ocisAdminServicePassword = adminPassword + if ocisAdminServicePassword == "" { + ocisAdminServicePassword, err = generators.GenerateRandomPassword(passwordLength) + if err != nil { + return fmt.Errorf("could not generate random password for ocis admin: %s", err) + } + } + + revaServicePassword, err = generators.GenerateRandomPassword(passwordLength) + if err != nil { + return fmt.Errorf("could not generate random password for reva: %s", err) + } + tokenManagerJwtSecret, err = generators.GenerateRandomPassword(passwordLength) + if err != nil { + return fmt.Errorf("could not generate random password for tokenmanager: %s", err) + } + collaborationWOPISecret, err = generators.GenerateRandomPassword(passwordLength) + if err != nil { + return fmt.Errorf("could not generate random wopi secret for collaboration service: %s", err) + } + machineAuthAPIKey, err = generators.GenerateRandomPassword(passwordLength) + if err != nil { + return fmt.Errorf("could not generate random password for machineauthsecret: %s", err) + } + systemUserAPIKey, err = generators.GenerateRandomPassword(passwordLength) + if err != nil { + return fmt.Errorf("could not generate random system user API key: %s", err) + } + revaTransferSecret, err = generators.GenerateRandomPassword(passwordLength) + if err != nil { + return fmt.Errorf("could not generate random password for revaTransferSecret: %s", err) + } + thumbnailsTransferSecret, err = generators.GenerateRandomPassword(passwordLength) + if err != nil { + return fmt.Errorf("could not generate random password for thumbnailsTransferSecret: %s", err) + } + serviceAccountSecret, err = generators.GenerateRandomPassword(passwordLength) + if err != nil { + return fmt.Errorf("could not generate random password for thumbnailsTransferSecret: %s", err) + } } serviceAccount := ServiceAccount{ @@ -445,7 +297,6 @@ func CreateConfig(insecure, forceOverwrite bool, configPath, adminPassword strin } if insecure { - cfg.AuthBearer = AuthbearerService{ AuthProviders: AuthProviderSettings{Oidc: _insecureService}, } @@ -472,16 +323,40 @@ func CreateConfig(insecure, forceOverwrite bool, configPath, adminPassword strin cfg.Thumbnails.Thumbnail.WebdavAllowInsecure = true cfg.Thumbnails.Thumbnail.Cs3AllowInsecure = true } - yamlOutput, err := yaml.Marshal(cfg) if err != nil { return fmt.Errorf("could not marshall config into yaml: %s", err) } - targetPath := path.Join(configPath, configFilename) - err = os.WriteFile(targetPath, yamlOutput, 0600) - if err != nil { - return err + if diff { + fmt.Println("running in diff mode") + tmpFile := path.Join(configPath, "ocis.yaml.tmp") + err = os.WriteFile(tmpFile, yamlOutput, 0600) + if err != nil { + return err + } + fmt.Println("diff -u " + path.Join(configPath, configFilename) + " " + tmpFile) + cmd := exec.Command("diff", "-u", path.Join(configPath, configFilename), tmpFile) + stdout, _ := cmd.Output() + fmt.Println(string(stdout)) + err = os.Remove(tmpFile) + patchPath := path.Join(configPath, "ocis.config.patch") + err = os.WriteFile(patchPath, stdout, 0600) + if err != nil { + return err + } + fmt.Printf("diff written to %s\n", patchPath) + } else { + targetPath := path.Join(configPath, configFilename) + err = os.WriteFile(targetPath, yamlOutput, 0600) + if err != nil { + return err + } + printBanner(targetPath, ocisAdminServicePassword, targetBackupConfig) } + return nil +} + +func printBanner(targetPath, ocisAdminServicePassword, targetBackupConfig string) { fmt.Printf( "\n=========================================\n"+ " generated OCIS Config\n"+ @@ -495,5 +370,4 @@ func CreateConfig(insecure, forceOverwrite bool, configPath, adminPassword strin "An older config file has been backuped to\n %s\n\n", targetBackupConfig) } - return nil } diff --git a/ocis/pkg/init/structs.go b/ocis/pkg/init/structs.go new file mode 100644 index 00000000000..49725526ac2 --- /dev/null +++ b/ocis/pkg/init/structs.go @@ -0,0 +1,213 @@ +package init + +type TokenManager struct { + JWTSecret string `yaml:"jwt_secret"` +} + +type InsecureService struct { + Insecure bool +} + +type ProxyService struct { + OIDC InsecureProxyOIDC `yaml:"oidc"` + InsecureBackends bool `yaml:"insecure_backends"` + ServiceAccount ServiceAccount `yaml:"service_account"` +} + +type InsecureProxyOIDC struct { + Insecure bool `yaml:"insecure"` +} + +type LdapSettings struct { + BindPassword string `yaml:"bind_password"` +} +type LdapBasedService struct { + Ldap LdapSettings +} + +type Events struct { + TLSInsecure bool `yaml:"tls_insecure"` +} +type GraphApplication struct { + ID string `yaml:"id"` +} + +type GraphService struct { + Application GraphApplication + Events Events + Spaces InsecureService + Identity LdapBasedService + ServiceAccount ServiceAccount `yaml:"service_account"` +} + +type ServiceUserPasswordsSettings struct { + AdminPassword string `yaml:"admin_password"` + IdmPassword string `yaml:"idm_password"` + RevaPassword string `yaml:"reva_password"` + IdpPassword string `yaml:"idp_password"` +} +type IdmService struct { + ServiceUserPasswords ServiceUserPasswordsSettings `yaml:"service_user_passwords"` +} + +type SettingsService struct { + ServiceAccountIDs []string `yaml:"service_account_ids"` +} + +type FrontendService struct { + AppHandler InsecureService `yaml:"app_handler"` + Archiver InsecureService + ServiceAccount ServiceAccount `yaml:"service_account"` +} + +type OcmService struct { + ServiceAccount ServiceAccount `yaml:"service_account"` +} + +type AuthbasicService struct { + AuthProviders LdapBasedService `yaml:"auth_providers"` +} + +type AuthProviderSettings struct { + Oidc InsecureService +} +type AuthbearerService struct { + AuthProviders AuthProviderSettings `yaml:"auth_providers"` +} + +type UsersAndGroupsService struct { + Drivers LdapBasedService +} + +type ThumbnailSettings struct { + TransferSecret string `yaml:"transfer_secret"` + WebdavAllowInsecure bool `yaml:"webdav_allow_insecure"` + Cs3AllowInsecure bool `yaml:"cs3_allow_insecure"` +} + +type ThumbnailService struct { + Thumbnail ThumbnailSettings +} + +type Search struct { + Events Events + ServiceAccount ServiceAccount `yaml:"service_account"` +} + +type Audit struct { + Events Events +} + +type Sharing struct { + Events Events +} + +type StorageUsers struct { + Events Events + MountID string `yaml:"mount_id"` + ServiceAccount ServiceAccount `yaml:"service_account"` +} + +type Gateway struct { + StorageRegistry StorageRegistry `yaml:"storage_registry"` +} + +type StorageRegistry struct { + StorageUsersMountID string `yaml:"storage_users_mount_id"` +} + +type Notifications struct { + Notifications struct{ Events Events } // The notifications config has a field called notifications + ServiceAccount ServiceAccount `yaml:"service_account"` +} + +type Userlog struct { + ServiceAccount ServiceAccount `yaml:"service_account"` +} + +type AuthService struct { + ServiceAccount ServiceAccount `yaml:"service_account"` +} + +type Clientlog struct { + ServiceAccount ServiceAccount `yaml:"service_account"` +} + +type WopiApp struct { + Secret string `yaml:"secret"` +} + +type App struct { + Insecure bool `yaml:"insecure"` +} + +type Collaboration struct { + WopiApp WopiApp `yaml:"wopi"` + App App `yaml:"app"` +} + +type Nats struct { + // The nats config has a field called nats + Nats struct { + TLSSkipVerifyClientCert bool `yaml:"tls_skip_verify_client_cert"` + } +} + +// Activitylog is the configuration for the activitylog service +type Activitylog struct { + ServiceAccount ServiceAccount `yaml:"service_account"` +} + +// ServiceAccount is the configuration for the used service account +type ServiceAccount struct { + ServiceAccountID string `yaml:"service_account_id"` + ServiceAccountSecret string `yaml:"service_account_secret"` +} + +// TODO: use the oCIS config struct instead of this custom struct +// We can't use it right now, because it would need "omitempty" on +// all elements, in order to produce a slim config file with `ocis init`. +// We can't just add these "omitempty" tags, since we want to generate +// full example configuration files with that struct, too. +// Proposed solution to get rid of this temporary solution: +// - use the oCIS config struct +// - set the needed values like below +// - marshal it to yaml +// - unmarshal it into yaml.Node +// - recurse through the nodes and delete empty / default ones +// - marshal it to yaml + +// OcisConfig is the configuration for the oCIS services +type OcisConfig struct { + TokenManager TokenManager `yaml:"token_manager"` + MachineAuthAPIKey string `yaml:"machine_auth_api_key"` + SystemUserAPIKey string `yaml:"system_user_api_key"` + TransferSecret string `yaml:"transfer_secret"` + SystemUserID string `yaml:"system_user_id"` + AdminUserID string `yaml:"admin_user_id"` + Graph GraphService `yaml:"graph"` + Idp LdapBasedService `yaml:"idp"` + Idm IdmService `yaml:"idm"` + Collaboration Collaboration `yaml:"collaboration"` + Proxy ProxyService `yaml:"proxy"` + Frontend FrontendService `yaml:"frontend"` + AuthBasic AuthbasicService `yaml:"auth_basic"` + AuthBearer AuthbearerService `yaml:"auth_bearer"` + Users UsersAndGroupsService `yaml:"users"` + Groups UsersAndGroupsService `yaml:"groups"` + Ocdav InsecureService `yaml:"ocdav"` + Ocm OcmService `yaml:"ocm"` + Thumbnails ThumbnailService `yaml:"thumbnails"` + Search Search `yaml:"search"` + Audit Audit `yaml:"audit"` + Settings SettingsService `yaml:"settings"` + Sharing Sharing `yaml:"sharing"` + StorageUsers StorageUsers `yaml:"storage_users"` + Notifications Notifications `yaml:"notifications"` + Nats Nats `yaml:"nats"` + Gateway Gateway `yaml:"gateway"` + Userlog Userlog `yaml:"userlog"` + AuthService AuthService `yaml:"auth_service"` + Clientlog Clientlog `yaml:"clientlog"` + Activitylog Activitylog `yaml:"activitylog"` +} From b60065f308380a9ef88910f192a98e17ee6b3967 Mon Sep 17 00:00:00 2001 From: Christian Richter Date: Fri, 26 Jul 2024 07:54:27 +0200 Subject: [PATCH 2/5] unclutter init pkg Signed-off-by: Christian Richter --- ocis/pkg/init/functions.go | 63 ++++++++++++++++++++++++++++++++++++++ ocis/pkg/init/init.go | 63 ++++---------------------------------- 2 files changed, 69 insertions(+), 57 deletions(-) create mode 100644 ocis/pkg/init/functions.go diff --git a/ocis/pkg/init/functions.go b/ocis/pkg/init/functions.go new file mode 100644 index 00000000000..dcb63292231 --- /dev/null +++ b/ocis/pkg/init/functions.go @@ -0,0 +1,63 @@ +package init + +import ( + "fmt" + "io" + "log" + "os" + "path" + "time" +) + +func checkConfigPath(configPath string) error { + targetPath := path.Join(configPath, configFilename) + if _, err := os.Stat(targetPath); err == nil { + return fmt.Errorf("config in %s already exists", targetPath) + } + return nil +} + +func configExists(configPath string) bool { + targetPath := path.Join(configPath, configFilename) + if _, err := os.Stat(targetPath); err == nil { + return true + } + return false +} + +func backupOcisConfigFile(configPath string) (string, error) { + sourceConfig := path.Join(configPath, configFilename) + targetBackupConfig := path.Join(configPath, configFilename+"."+time.Now().Format("2006-01-02-15-04-05")+".backup") + source, err := os.Open(sourceConfig) + if err != nil { + log.Fatalf("Could not read %s (%s)", sourceConfig, err) + } + defer source.Close() + target, err := os.Create(targetBackupConfig) + if err != nil { + log.Fatalf("Could not generate backup %s (%s)", targetBackupConfig, err) + } + defer target.Close() + _, err = io.Copy(target, source) + if err != nil { + log.Fatalf("Could not write backup %s (%s)", targetBackupConfig, err) + } + return targetBackupConfig, nil +} + +// printBanner prints the generated OCIS config banner. +func printBanner(targetPath, ocisAdminServicePassword, targetBackupConfig string) { + fmt.Printf( + "\n=========================================\n"+ + " generated OCIS Config\n"+ + "=========================================\n"+ + " configpath : %s\n"+ + " user : admin\n"+ + " password : %s\n\n", + targetPath, ocisAdminServicePassword) + if targetBackupConfig != "" { + fmt.Printf("\n=========================================\n"+ + "An older config file has been backuped to\n %s\n\n", + targetBackupConfig) + } +} diff --git a/ocis/pkg/init/init.go b/ocis/pkg/init/init.go index 8391e9b5831..bc73d0d86f7 100644 --- a/ocis/pkg/init/init.go +++ b/ocis/pkg/init/init.go @@ -2,14 +2,11 @@ package init import ( "fmt" - "github.com/gofrs/uuid" - "io" - "log" "os" "os/exec" "path" - "time" + "github.com/gofrs/uuid" "github.com/owncloud/ocis/v2/ocis-pkg/generators" "gopkg.in/yaml.v2" ) @@ -24,42 +21,6 @@ var ( _insecureEvents = Events{TLSInsecure: true} ) -func checkConfigPath(configPath string) error { - targetPath := path.Join(configPath, configFilename) - if _, err := os.Stat(targetPath); err == nil { - return fmt.Errorf("config in %s already exists", targetPath) - } - return nil -} - -func configExists(configPath string) bool { - targetPath := path.Join(configPath, configFilename) - if _, err := os.Stat(targetPath); err == nil { - return true - } - return false -} - -func backupOcisConfigFile(configPath string) (string, error) { - sourceConfig := path.Join(configPath, configFilename) - targetBackupConfig := path.Join(configPath, configFilename+"."+time.Now().Format("2006-01-02-15-04-05")+".backup") - source, err := os.Open(sourceConfig) - if err != nil { - log.Fatalf("Could not read %s (%s)", sourceConfig, err) - } - defer source.Close() - target, err := os.Create(targetBackupConfig) - if err != nil { - log.Fatalf("Could not generate backup %s (%s)", targetBackupConfig, err) - } - defer target.Close() - _, err = io.Copy(target, source) - if err != nil { - log.Fatalf("Could not write backup %s (%s)", targetBackupConfig, err) - } - return targetBackupConfig, nil -} - // CreateConfig creates a config file with random passwords at configPath func CreateConfig(insecure, forceOverwrite, diff bool, configPath, adminPassword string) error { if diff { @@ -336,7 +297,11 @@ func CreateConfig(insecure, forceOverwrite, diff bool, configPath, adminPassword } fmt.Println("diff -u " + path.Join(configPath, configFilename) + " " + tmpFile) cmd := exec.Command("diff", "-u", path.Join(configPath, configFilename), tmpFile) - stdout, _ := cmd.Output() + stdout, err := cmd.Output() + if err == nil { + fmt.Println("no changes, your config is up to date") + return nil + } fmt.Println(string(stdout)) err = os.Remove(tmpFile) patchPath := path.Join(configPath, "ocis.config.patch") @@ -355,19 +320,3 @@ func CreateConfig(insecure, forceOverwrite, diff bool, configPath, adminPassword } return nil } - -func printBanner(targetPath, ocisAdminServicePassword, targetBackupConfig string) { - fmt.Printf( - "\n=========================================\n"+ - " generated OCIS Config\n"+ - "=========================================\n"+ - " configpath : %s\n"+ - " user : admin\n"+ - " password : %s\n\n", - targetPath, ocisAdminServicePassword) - if targetBackupConfig != "" { - fmt.Printf("\n=========================================\n"+ - "An older config file has been backuped to\n %s\n\n", - targetBackupConfig) - } -} From 745d0d283820437a94af41215e69bc657231b76f Mon Sep 17 00:00:00 2001 From: Christian Richter Date: Fri, 26 Jul 2024 07:57:45 +0200 Subject: [PATCH 3/5] add changelog Signed-off-by: Christian Richter --- changelog/unreleased/initdiffmode.md | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 changelog/unreleased/initdiffmode.md diff --git a/changelog/unreleased/initdiffmode.md b/changelog/unreleased/initdiffmode.md new file mode 100644 index 00000000000..a524797d5e0 --- /dev/null +++ b/changelog/unreleased/initdiffmode.md @@ -0,0 +1,9 @@ +Enhancement: Add `--diff` to the `ocis init` command + +We have added a new flag `--diff` to the `ocis init` command to show the diff of the configuration files. +This is useful to see what has changed in the configuration files when you run the `ocis init` command. +The diff is stored to the ocispath in the config folder as ocis.config.patch and can be applied using the +linux `patch` command. + +https://github.com/owncloud/ocis/pull/9693 +https://github.com/owncloud/ocis/issues/3645 From a04cd2a132c4b81a1b2c6b1afdac57b12c462193 Mon Sep 17 00:00:00 2001 From: Christian Richter Date: Fri, 26 Jul 2024 08:55:40 +0200 Subject: [PATCH 4/5] sort structs Signed-off-by: Christian Richter --- ocis/pkg/init/init.go | 8 +- ocis/pkg/init/structs.go | 304 ++++++++++++++++++++++----------------- 2 files changed, 178 insertions(+), 134 deletions(-) diff --git a/ocis/pkg/init/init.go b/ocis/pkg/init/init.go index bc73d0d86f7..2a399f7d28f 100644 --- a/ocis/pkg/init/init.go +++ b/ocis/pkg/init/init.go @@ -12,7 +12,7 @@ import ( ) const ( - configFilename = "ocis.yaml" // TODO: use also a constant for reading this file + configFilename = "ocis.yaml" passwordLength = 32 ) @@ -63,6 +63,9 @@ func CreateConfig(insecure, forceOverwrite, diff bool, configPath, adminPassword return err } err = yaml.Unmarshal(fp, &oldCfg) + if err != nil { + return err + } } var ( @@ -304,6 +307,9 @@ func CreateConfig(insecure, forceOverwrite, diff bool, configPath, adminPassword } fmt.Println(string(stdout)) err = os.Remove(tmpFile) + if err != nil { + return err + } patchPath := path.Join(configPath, "ocis.config.patch") err = os.WriteFile(patchPath, stdout, 0600) if err != nil { diff --git a/ocis/pkg/init/structs.go b/ocis/pkg/init/structs.go index 49725526ac2..383aeabea11 100644 --- a/ocis/pkg/init/structs.go +++ b/ocis/pkg/init/structs.go @@ -1,37 +1,122 @@ package init -type TokenManager struct { - JWTSecret string `yaml:"jwt_secret"` -} +// TODO: use the oCIS config struct instead of this custom struct +// We can't use it right now, because it would need "omitempty" on +// all elements, in order to produce a slim config file with `ocis init`. +// We can't just add these "omitempty" tags, since we want to generate +// full example configuration files with that struct, too. +// Proposed solution to get rid of this temporary solution: +// - use the oCIS config struct +// - set the needed values like below +// - marshal it to yaml +// - unmarshal it into yaml.Node +// - recurse through the nodes and delete empty / default ones +// - marshal it to yaml -type InsecureService struct { - Insecure bool +// OcisConfig is the configuration for the oCIS services +type OcisConfig struct { + TokenManager TokenManager `yaml:"token_manager"` + MachineAuthAPIKey string `yaml:"machine_auth_api_key"` + SystemUserAPIKey string `yaml:"system_user_api_key"` + TransferSecret string `yaml:"transfer_secret"` + SystemUserID string `yaml:"system_user_id"` + AdminUserID string `yaml:"admin_user_id"` + Graph GraphService `yaml:"graph"` + Idp LdapBasedService `yaml:"idp"` + Idm IdmService `yaml:"idm"` + Collaboration Collaboration `yaml:"collaboration"` + Proxy ProxyService `yaml:"proxy"` + Frontend FrontendService `yaml:"frontend"` + AuthBasic AuthbasicService `yaml:"auth_basic"` + AuthBearer AuthbearerService `yaml:"auth_bearer"` + Users UsersAndGroupsService `yaml:"users"` + Groups UsersAndGroupsService `yaml:"groups"` + Ocdav InsecureService `yaml:"ocdav"` + Ocm OcmService `yaml:"ocm"` + Thumbnails ThumbnailService `yaml:"thumbnails"` + Search Search `yaml:"search"` + Audit Audit `yaml:"audit"` + Settings SettingsService `yaml:"settings"` + Sharing Sharing `yaml:"sharing"` + StorageUsers StorageUsers `yaml:"storage_users"` + Notifications Notifications `yaml:"notifications"` + Nats Nats `yaml:"nats"` + Gateway Gateway `yaml:"gateway"` + Userlog Userlog `yaml:"userlog"` + AuthService AuthService `yaml:"auth_service"` + Clientlog Clientlog `yaml:"clientlog"` + Activitylog Activitylog `yaml:"activitylog"` } -type ProxyService struct { - OIDC InsecureProxyOIDC `yaml:"oidc"` - InsecureBackends bool `yaml:"insecure_backends"` - ServiceAccount ServiceAccount `yaml:"service_account"` +// Activitylog is the configuration for the activitylog service +type Activitylog struct { + ServiceAccount ServiceAccount `yaml:"service_account"` } -type InsecureProxyOIDC struct { +// App is the configuration for the collaboration service +type App struct { Insecure bool `yaml:"insecure"` } -type LdapSettings struct { - BindPassword string `yaml:"bind_password"` +// Audit is the configuration for the audit service +type Audit struct { + Events Events } -type LdapBasedService struct { - Ldap LdapSettings + +// AuthbasicService is the configuration for the authbasic service +type AuthbasicService struct { + AuthProviders LdapBasedService `yaml:"auth_providers"` } +// AuthbearerService is the configuration for the authbearer service +type AuthbearerService struct { + AuthProviders AuthProviderSettings `yaml:"auth_providers"` +} + +// AuthProviderSettings is the configuration for the auth provider settings +type AuthProviderSettings struct { + Oidc InsecureService +} + +// AuthService is the configuration for the auth service +type AuthService struct { + ServiceAccount ServiceAccount `yaml:"service_account"` +} + +// Clientlog is the configuration for the clientlog service +type Clientlog struct { + ServiceAccount ServiceAccount `yaml:"service_account"` +} + +// Collaboration is the configuration for the collaboration service +type Collaboration struct { + WopiApp WopiApp `yaml:"wopi"` + App App `yaml:"app"` +} + +// Events is the configuration for events type Events struct { TLSInsecure bool `yaml:"tls_insecure"` } + +// FrontendService is the configuration for the frontend service +type FrontendService struct { + AppHandler InsecureService `yaml:"app_handler"` + Archiver InsecureService + ServiceAccount ServiceAccount `yaml:"service_account"` +} + +// Gateway is the configuration for the gateway +type Gateway struct { + StorageRegistry StorageRegistry `yaml:"storage_registry"` +} + +// GraphApplication is the configuration for the graph application type GraphApplication struct { ID string `yaml:"id"` } +// GraphService is the configuration for the graph service type GraphService struct { Application GraphApplication Events Events @@ -40,174 +125,127 @@ type GraphService struct { ServiceAccount ServiceAccount `yaml:"service_account"` } -type ServiceUserPasswordsSettings struct { - AdminPassword string `yaml:"admin_password"` - IdmPassword string `yaml:"idm_password"` - RevaPassword string `yaml:"reva_password"` - IdpPassword string `yaml:"idp_password"` -} +// IdmService is the configuration for the IDM service type IdmService struct { ServiceUserPasswords ServiceUserPasswordsSettings `yaml:"service_user_passwords"` } -type SettingsService struct { - ServiceAccountIDs []string `yaml:"service_account_ids"` +// InsecureProxyOIDC is the configuration for the insecure proxy OIDC +type InsecureProxyOIDC struct { + Insecure bool `yaml:"insecure"` } -type FrontendService struct { - AppHandler InsecureService `yaml:"app_handler"` - Archiver InsecureService - ServiceAccount ServiceAccount `yaml:"service_account"` +// InsecureService is the configuration for services that can be insecure +type InsecureService struct { + Insecure bool } -type OcmService struct { - ServiceAccount ServiceAccount `yaml:"service_account"` +// LdapBasedService is the configuration for LDAP based services +type LdapBasedService struct { + Ldap LdapSettings } -type AuthbasicService struct { - AuthProviders LdapBasedService `yaml:"auth_providers"` +// LdapSettings is the configuration for LDAP settings +type LdapSettings struct { + BindPassword string `yaml:"bind_password"` } -type AuthProviderSettings struct { - Oidc InsecureService -} -type AuthbearerService struct { - AuthProviders AuthProviderSettings `yaml:"auth_providers"` +// Nats is the configuration for the nats service +type Nats struct { + // The nats config has a field called nats + Nats struct { + TLSSkipVerifyClientCert bool `yaml:"tls_skip_verify_client_cert"` + } } -type UsersAndGroupsService struct { - Drivers LdapBasedService +// Notifications is the configuration for the notifications service +type Notifications struct { + Notifications struct{ Events Events } // The notifications config has a field called notifications + ServiceAccount ServiceAccount `yaml:"service_account"` } -type ThumbnailSettings struct { - TransferSecret string `yaml:"transfer_secret"` - WebdavAllowInsecure bool `yaml:"webdav_allow_insecure"` - Cs3AllowInsecure bool `yaml:"cs3_allow_insecure"` +// OcmService is the configuration for the OCM service +type OcmService struct { + ServiceAccount ServiceAccount `yaml:"service_account"` } -type ThumbnailService struct { - Thumbnail ThumbnailSettings +// ProxyService is the configuration for the proxy service +type ProxyService struct { + OIDC InsecureProxyOIDC `yaml:"oidc"` + InsecureBackends bool `yaml:"insecure_backends"` + ServiceAccount ServiceAccount `yaml:"service_account"` } +// Search is the configuration for the search service type Search struct { Events Events ServiceAccount ServiceAccount `yaml:"service_account"` } -type Audit struct { - Events Events +// ServiceAccount is the configuration for the used service account +type ServiceAccount struct { + ServiceAccountID string `yaml:"service_account_id"` + ServiceAccountSecret string `yaml:"service_account_secret"` } -type Sharing struct { - Events Events +// ServiceUserPasswordsSettings is the configuration for service user passwords +type ServiceUserPasswordsSettings struct { + AdminPassword string `yaml:"admin_password"` + IdmPassword string `yaml:"idm_password"` + RevaPassword string `yaml:"reva_password"` + IdpPassword string `yaml:"idp_password"` } -type StorageUsers struct { - Events Events - MountID string `yaml:"mount_id"` - ServiceAccount ServiceAccount `yaml:"service_account"` +// SettingsService is the configuration for the settings service +type SettingsService struct { + ServiceAccountIDs []string `yaml:"service_account_ids"` } -type Gateway struct { - StorageRegistry StorageRegistry `yaml:"storage_registry"` +// Sharing is the configuration for the sharing service +type Sharing struct { + Events Events } +// StorageRegistry is the configuration for the storage registry type StorageRegistry struct { StorageUsersMountID string `yaml:"storage_users_mount_id"` } -type Notifications struct { - Notifications struct{ Events Events } // The notifications config has a field called notifications - ServiceAccount ServiceAccount `yaml:"service_account"` -} - -type Userlog struct { - ServiceAccount ServiceAccount `yaml:"service_account"` -} - -type AuthService struct { - ServiceAccount ServiceAccount `yaml:"service_account"` -} - -type Clientlog struct { +// StorageUsers is the configuration for the storage users +type StorageUsers struct { + Events Events + MountID string `yaml:"mount_id"` ServiceAccount ServiceAccount `yaml:"service_account"` } -type WopiApp struct { - Secret string `yaml:"secret"` +// ThumbnailSettings is the configuration for the thumbnail settings +type ThumbnailSettings struct { + TransferSecret string `yaml:"transfer_secret"` + WebdavAllowInsecure bool `yaml:"webdav_allow_insecure"` + Cs3AllowInsecure bool `yaml:"cs3_allow_insecure"` } -type App struct { - Insecure bool `yaml:"insecure"` +// ThumbnailService is the configuration for the thumbnail service +type ThumbnailService struct { + Thumbnail ThumbnailSettings } -type Collaboration struct { - WopiApp WopiApp `yaml:"wopi"` - App App `yaml:"app"` +// TokenManager is the configuration for the token manager +type TokenManager struct { + JWTSecret string `yaml:"jwt_secret"` } -type Nats struct { - // The nats config has a field called nats - Nats struct { - TLSSkipVerifyClientCert bool `yaml:"tls_skip_verify_client_cert"` - } +// UsersAndGroupsService is the configuration for the users and groups service +type UsersAndGroupsService struct { + Drivers LdapBasedService } -// Activitylog is the configuration for the activitylog service -type Activitylog struct { +// Userlog is the configuration for the userlog service +type Userlog struct { ServiceAccount ServiceAccount `yaml:"service_account"` } -// ServiceAccount is the configuration for the used service account -type ServiceAccount struct { - ServiceAccountID string `yaml:"service_account_id"` - ServiceAccountSecret string `yaml:"service_account_secret"` -} - -// TODO: use the oCIS config struct instead of this custom struct -// We can't use it right now, because it would need "omitempty" on -// all elements, in order to produce a slim config file with `ocis init`. -// We can't just add these "omitempty" tags, since we want to generate -// full example configuration files with that struct, too. -// Proposed solution to get rid of this temporary solution: -// - use the oCIS config struct -// - set the needed values like below -// - marshal it to yaml -// - unmarshal it into yaml.Node -// - recurse through the nodes and delete empty / default ones -// - marshal it to yaml - -// OcisConfig is the configuration for the oCIS services -type OcisConfig struct { - TokenManager TokenManager `yaml:"token_manager"` - MachineAuthAPIKey string `yaml:"machine_auth_api_key"` - SystemUserAPIKey string `yaml:"system_user_api_key"` - TransferSecret string `yaml:"transfer_secret"` - SystemUserID string `yaml:"system_user_id"` - AdminUserID string `yaml:"admin_user_id"` - Graph GraphService `yaml:"graph"` - Idp LdapBasedService `yaml:"idp"` - Idm IdmService `yaml:"idm"` - Collaboration Collaboration `yaml:"collaboration"` - Proxy ProxyService `yaml:"proxy"` - Frontend FrontendService `yaml:"frontend"` - AuthBasic AuthbasicService `yaml:"auth_basic"` - AuthBearer AuthbearerService `yaml:"auth_bearer"` - Users UsersAndGroupsService `yaml:"users"` - Groups UsersAndGroupsService `yaml:"groups"` - Ocdav InsecureService `yaml:"ocdav"` - Ocm OcmService `yaml:"ocm"` - Thumbnails ThumbnailService `yaml:"thumbnails"` - Search Search `yaml:"search"` - Audit Audit `yaml:"audit"` - Settings SettingsService `yaml:"settings"` - Sharing Sharing `yaml:"sharing"` - StorageUsers StorageUsers `yaml:"storage_users"` - Notifications Notifications `yaml:"notifications"` - Nats Nats `yaml:"nats"` - Gateway Gateway `yaml:"gateway"` - Userlog Userlog `yaml:"userlog"` - AuthService AuthService `yaml:"auth_service"` - Clientlog Clientlog `yaml:"clientlog"` - Activitylog Activitylog `yaml:"activitylog"` +// WopiApp is the configuration for the WOPI app +type WopiApp struct { + Secret string `yaml:"secret"` } From 201a7c6b0125761db91e4665b34c0d60c508c0d9 Mon Sep 17 00:00:00 2001 From: Christian Richter Date: Fri, 26 Jul 2024 10:34:11 +0200 Subject: [PATCH 5/5] reduce complexity Signed-off-by: Christian Richter --- ocis/pkg/init/functions.go | 41 +++++++++++++++++++++++++++++ ocis/pkg/init/init.go | 53 +++++++------------------------------- 2 files changed, 50 insertions(+), 44 deletions(-) diff --git a/ocis/pkg/init/functions.go b/ocis/pkg/init/functions.go index dcb63292231..a76fda1202d 100644 --- a/ocis/pkg/init/functions.go +++ b/ocis/pkg/init/functions.go @@ -5,6 +5,7 @@ import ( "io" "log" "os" + "os/exec" "path" "time" ) @@ -61,3 +62,43 @@ func printBanner(targetPath, ocisAdminServicePassword, targetBackupConfig string targetBackupConfig) } } + +// writeConfig writes the config to the target path and prints a banner +func writeConfig(configPath, ocisAdminServicePassword, targetBackupConfig string, yamlOutput []byte) error { + targetPath := path.Join(configPath, configFilename) + err := os.WriteFile(targetPath, yamlOutput, 0600) + if err != nil { + return err + } + printBanner(targetPath, ocisAdminServicePassword, targetBackupConfig) + return nil +} + +// writePatch writes the diff to a file +func writePatch(configPath string, yamlOutput []byte) error { + fmt.Println("running in diff mode") + tmpFile := path.Join(configPath, "ocis.yaml.tmp") + err := os.WriteFile(tmpFile, yamlOutput, 0600) + if err != nil { + return err + } + fmt.Println("diff -u " + path.Join(configPath, configFilename) + " " + tmpFile) + cmd := exec.Command("diff", "-u", path.Join(configPath, configFilename), tmpFile) + stdout, err := cmd.Output() + if err == nil { + fmt.Println("no changes, your config is up to date") + return nil + } + fmt.Println(string(stdout)) + err = os.Remove(tmpFile) + if err != nil { + return err + } + patchPath := path.Join(configPath, "ocis.config.patch") + err = os.WriteFile(patchPath, stdout, 0600) + if err != nil { + return err + } + fmt.Printf("diff written to %s\n", patchPath) + return nil +} diff --git a/ocis/pkg/init/init.go b/ocis/pkg/init/init.go index 2a399f7d28f..5c837774f3c 100644 --- a/ocis/pkg/init/init.go +++ b/ocis/pkg/init/init.go @@ -3,7 +3,6 @@ package init import ( "fmt" "os" - "os/exec" "path" "github.com/gofrs/uuid" @@ -23,19 +22,15 @@ var ( // CreateConfig creates a config file with random passwords at configPath func CreateConfig(insecure, forceOverwrite, diff bool, configPath, adminPassword string) error { - if diff { - if forceOverwrite { - return fmt.Errorf("diff and force-overwrite flags are mutually exclusive") - } - if adminPassword != "" { - return fmt.Errorf("diff and admin-password flags are mutually exclusive") - } + if diff && forceOverwrite { + return fmt.Errorf("diff and force-overwrite flags are mutually exclusive") + } + if diff && adminPassword != "" { + return fmt.Errorf("diff and admin-password flags are mutually exclusive") } - if configExists(configPath) { - if !forceOverwrite && !diff { - return fmt.Errorf("config file already exists, use --force-overwrite to overwrite or --diff to show diff") - } + if configExists(configPath) && !forceOverwrite && !diff { + return fmt.Errorf("config file already exists, use --force-overwrite to overwrite or --diff to show diff") } err := checkConfigPath(configPath) @@ -292,37 +287,7 @@ func CreateConfig(insecure, forceOverwrite, diff bool, configPath, adminPassword return fmt.Errorf("could not marshall config into yaml: %s", err) } if diff { - fmt.Println("running in diff mode") - tmpFile := path.Join(configPath, "ocis.yaml.tmp") - err = os.WriteFile(tmpFile, yamlOutput, 0600) - if err != nil { - return err - } - fmt.Println("diff -u " + path.Join(configPath, configFilename) + " " + tmpFile) - cmd := exec.Command("diff", "-u", path.Join(configPath, configFilename), tmpFile) - stdout, err := cmd.Output() - if err == nil { - fmt.Println("no changes, your config is up to date") - return nil - } - fmt.Println(string(stdout)) - err = os.Remove(tmpFile) - if err != nil { - return err - } - patchPath := path.Join(configPath, "ocis.config.patch") - err = os.WriteFile(patchPath, stdout, 0600) - if err != nil { - return err - } - fmt.Printf("diff written to %s\n", patchPath) - } else { - targetPath := path.Join(configPath, configFilename) - err = os.WriteFile(targetPath, yamlOutput, 0600) - if err != nil { - return err - } - printBanner(targetPath, ocisAdminServicePassword, targetBackupConfig) + return writePatch(configPath, yamlOutput) } - return nil + return writeConfig(configPath, ocisAdminServicePassword, targetBackupConfig, yamlOutput) }