From 001d0f14b73858191dffaa5a0d5a7305dec507a4 Mon Sep 17 00:00:00 2001 From: Dominik Przybyl Date: Thu, 21 Nov 2024 10:22:51 +0100 Subject: [PATCH 1/9] added new instance filters --- cmd/aem/root.go | 19 +++++++++++--- pkg/instance_manager.go | 57 +++++++++++++++++++++++------------------ 2 files changed, 47 insertions(+), 29 deletions(-) diff --git a/cmd/aem/root.go b/cmd/aem/root.go index ca77954d..1a29a7af 100644 --- a/cmd/aem/root.go +++ b/cmd/aem/root.go @@ -71,16 +71,27 @@ func (c *CLI) rootFlags(cmd *cobra.Command) { cmd.PersistentFlags().StringSliceP("instance-url", "U", cv.GetStringSlice("instance.adhoc_url"), "Use only AEM instance(s) at ad-hoc specified URL(s)") _ = cv.BindPFlag("instance.adhoc_url", cmd.PersistentFlags().Lookup("instance-url")) - cmd.PersistentFlags().StringP("instance-id", "I", cv.GetString("instance.filter.id"), "Use only AEM instance configured with the exact ID") + cmd.PersistentFlags().StringSliceP("instance-id", "I", cv.GetStringSlice("instance.filter.id"), "Use only AEM instance(s) configured with the exact ID") _ = cv.BindPFlag("instance.filter.id", cmd.PersistentFlags().Lookup("instance-id")) - cmd.PersistentFlags().BoolP("instance-author", "A", cv.GetBool("instance.filter.authors"), "Use only AEM author instance") + cmd.PersistentFlags().BoolP("instance-author", "A", cv.GetBool("instance.filter.authors"), "Use only AEM author instance(s)") _ = cv.BindPFlag("instance.filter.authors", cmd.PersistentFlags().Lookup("instance-author")) - cmd.PersistentFlags().BoolP("instance-publish", "P", cv.GetBool("instance.filter.publishes"), "Use only AEM publish instance") + cmd.PersistentFlags().BoolP("instance-publish", "P", cv.GetBool("instance.filter.publishes"), "Use only AEM publish instance(s)") _ = cv.BindPFlag("instance.filter.publishes", cmd.PersistentFlags().Lookup("instance-publish")) - cmd.MarkFlagsMutuallyExclusive("instance-author", "instance-publish") + cmd.PersistentFlags().BoolP("instance-local", "L", cv.GetBool("instance.filter.locals"), "Use only AEM local instance(s)") + _ = cv.BindPFlag("instance.filter.locals", cmd.PersistentFlags().Lookup("instance-local")) + + cmd.PersistentFlags().BoolP("instance-remote", "R", cv.GetBool("instance.filter.remotes"), "Use only AEM remote instance(s)") + _ = cv.BindPFlag("instance.filter.remotes", cmd.PersistentFlags().Lookup("instance-remote")) + + cmd.PersistentFlags().StringP("instance-classifier-prefix", "C", cv.GetString("instance.filter.classifier-prefix"), "Use only AEM instance(s) with proper classifier prefix") + _ = cv.BindPFlag("instance.filter.classifier-prefix", cmd.PersistentFlags().Lookup("instance-classifier-prefix")) + + cmd.MarkFlagsMutuallyExclusive("instance-url", "instance-id", "instance-author", "instance-publish") + cmd.MarkFlagsMutuallyExclusive("instance-url", "instance-id", "instance-local", "instance-remote") + cmd.MarkFlagsMutuallyExclusive("instance-url", "instance-id", "instance-classifier-prefix") cmd.PersistentFlags().String("instance-processing", cv.GetString("instance.processing_mode"), "Controls processing mode for instances ("+(strings.Join(instance.ProcessingModes(), "|")+")")) _ = cv.BindPFlag("instance.processing_mode", cmd.PersistentFlags().Lookup("instance-processing")) diff --git a/pkg/instance_manager.go b/pkg/instance_manager.go index 893ee774..02a54f9e 100644 --- a/pkg/instance_manager.go +++ b/pkg/instance_manager.go @@ -21,10 +21,13 @@ type InstanceManager struct { AdHocURLs []string - FilterID string - FilterAuthors bool - FilterPublishes bool - ProcessingMode string + FilterIDs []string + FilterAuthors bool + FilterPublishes bool + FilterLocals bool + FilterRemotes bool + FilterClassifierPrefix string + ProcessingMode string } func NewInstanceManager(aem *AEM) *InstanceManager { @@ -35,9 +38,20 @@ func NewInstanceManager(aem *AEM) *InstanceManager { result.AdHocURLs = cv.GetStringSlice("instance.adhoc_url") - result.FilterID = cv.GetString("instance.filter.id") + result.FilterIDs = cv.GetStringSlice("instance.filter.id") result.FilterAuthors = cv.GetBool("instance.filter.authors") result.FilterPublishes = cv.GetBool("instance.filter.publishes") + if !result.FilterAuthors && !result.FilterPublishes { + result.FilterAuthors = true + result.FilterPublishes = true + } + result.FilterLocals = cv.GetBool("instance.filter.locals") + result.FilterRemotes = cv.GetBool("instance.filter.remotes") + if !result.FilterLocals && !result.FilterRemotes { + result.FilterLocals = true + result.FilterRemotes = true + } + result.FilterClassifierPrefix = cv.GetString("instance.filter.classifier-prefix") result.ProcessingMode = cv.GetString("instance.processing_mode") result.LocalOpts = NewLocalOpts(result) @@ -165,33 +179,26 @@ func (im *InstanceManager) newFromConfig(id string) *Instance { func (im *InstanceManager) filter(instances []Instance) []Instance { result := []Instance{} - if im.FilterID != "" { + if len(im.FilterIDs) > 0 { for _, i := range instances { - if i.id == im.FilterID { - result = append(result, i) - break + for _, filterID := range im.FilterIDs { + if i.id == filterID { + result = append(result, i) + break + } } } } else { - if im.FilterAuthors == im.FilterPublishes { - result = instances - } else { - if im.FilterAuthors { - for _, i := range instances { - if i.IsAuthor() { - result = append(result, i) - } - } - } - if im.FilterPublishes { - for _, i := range instances { - if i.IsPublish() { - result = append(result, i) - } - } + for _, i := range instances { + filterLocation := im.FilterAuthors && i.IsAuthor() || im.FilterPublishes && i.IsPublish() + filterRole := im.FilterLocals && i.IsLocal() || im.FilterRemotes && i.IsRemote() + filterClassifier := strings.HasPrefix(i.IDInfo().Classifier, im.FilterClassifierPrefix) + if filterLocation && filterRole && filterClassifier { + result = append(result, i) } } } + sort.SliceStable(result, func(i, j int) bool { return strings.Compare(result[i].id, result[j].id) < 0 }) From d2f072ef305bb0e434009c34cd3b1b73132deade Mon Sep 17 00:00:00 2001 From: Dominik Przybyl Date: Thu, 21 Nov 2024 11:21:10 +0100 Subject: [PATCH 2/9] minor --- pkg/instance_manager.go | 1 - 1 file changed, 1 deletion(-) diff --git a/pkg/instance_manager.go b/pkg/instance_manager.go index 02a54f9e..3add1fce 100644 --- a/pkg/instance_manager.go +++ b/pkg/instance_manager.go @@ -198,7 +198,6 @@ func (im *InstanceManager) filter(instances []Instance) []Instance { } } } - sort.SliceStable(result, func(i, j int) bool { return strings.Compare(result[i].id, result[j].id) < 0 }) From da1c5ae9131e7cb0a445297e0d689d8b2dfb102a Mon Sep 17 00:00:00 2001 From: Dominik Przybyl Date: Fri, 22 Nov 2024 00:00:01 +0100 Subject: [PATCH 3/9] added tests --- cmd/aem/instance_list_int_test.go | 119 +++++++++++ pkg/instance_manager.go | 2 +- test/resources/aem.yml | 331 ++++++++++++++++++++++++++++++ 3 files changed, 451 insertions(+), 1 deletion(-) create mode 100644 cmd/aem/instance_list_int_test.go create mode 100755 test/resources/aem.yml diff --git a/cmd/aem/instance_list_int_test.go b/cmd/aem/instance_list_int_test.go new file mode 100644 index 00000000..4fad068b --- /dev/null +++ b/cmd/aem/instance_list_int_test.go @@ -0,0 +1,119 @@ +//go:build int_test + +package main + +import ( + "github.com/wttech/aemc/pkg" + "github.com/wttech/aemc/pkg/cfg" + "os" + "sort" + "strings" + "testing" +) + +func testInstanceList(t *testing.T, args []string, expectedResult bool, expectedIDs []string) { + if err := os.Setenv(cfg.FileEnvVar, "../../test/resources/aem.yml"); err != nil { + t.Fatal(err) + } + + cli := NewCLI() + cmd := cli.cmd + cmd.SetArgs(args) + + defer func() { + err := recover() + actualResult := err != nil + if actualResult != expectedResult { + t.Errorf("InstanceList(%v) = %v; want %v", args, actualResult, expectedResult) + } else if actualResult && expectedResult { + actualIDs := []string{} + instances := cli.outputResponse.Data["instances"].([]pkg.Instance) + for _, i := range instances { + actualIDs = append(actualIDs, i.ID()) + } + sort.SliceStable(actualIDs, func(i, j int) bool { + return strings.Compare(actualIDs[i], actualIDs[j]) < 0 + }) + sort.SliceStable(expectedIDs, func(i, j int) bool { + return strings.Compare(expectedIDs[i], expectedIDs[j]) < 0 + }) + result := len(actualIDs) == len(expectedIDs) + for i := range actualIDs { + result = result && actualIDs[i] == expectedIDs[i] + } + if !result { + t.Errorf("InstanceList(%v) = %v; want %v", args, actualIDs, expectedIDs) + } + } + }() + + _ = cmd.Execute() +} + +func TestAllInstances(t *testing.T) { + testInstanceList(t, []string{"instance", "list"}, true, []string{"local_author", "local_publish", "remote_author_dev-auth", "remote_publish_dev-pub1", "remote_publish_dev-pub2"}) +} + +func TestAuthorInstances(t *testing.T) { + testInstanceList(t, []string{"instance", "list", "-A"}, true, []string{"local_author", "remote_author_dev-auth"}) +} + +func TestPublishInstances(t *testing.T) { + testInstanceList(t, []string{"instance", "list", "-P"}, true, []string{"local_publish", "remote_publish_dev-pub1", "remote_publish_dev-pub2"}) +} + +func TestLocalInstances(t *testing.T) { + testInstanceList(t, []string{"instance", "list", "-L"}, true, []string{"local_author", "local_publish"}) +} + +func TestLocalAuthorInstances(t *testing.T) { + testInstanceList(t, []string{"instance", "list", "-L", "-A"}, true, []string{"local_author"}) +} + +func TestRemoteInstances(t *testing.T) { + testInstanceList(t, []string{"instance", "list", "-R"}, true, []string{"remote_author_dev-auth", "remote_publish_dev-pub1", "remote_publish_dev-pub2"}) +} + +func TestRemotePublishInstances(t *testing.T) { + testInstanceList(t, []string{"instance", "list", "-R", "-P"}, true, []string{"remote_publish_dev-pub1", "remote_publish_dev-pub2"}) +} + +func TestDevInstances(t *testing.T) { + testInstanceList(t, []string{"instance", "list", "-C", "dev"}, true, []string{"remote_author_dev-auth", "remote_publish_dev-pub1", "remote_publish_dev-pub2"}) +} + +func TestPublishDevInstances(t *testing.T) { + testInstanceList(t, []string{"instance", "list", "-P", "-C", "dev"}, true, []string{"remote_publish_dev-pub1", "remote_publish_dev-pub2"}) +} + +func TestInstanceByID(t *testing.T) { + testInstanceList(t, []string{"instance", "list", "-I", "local_author", "-I", "remote_publish_dev-pub2"}, true, []string{"local_author", "remote_publish_dev-pub2"}) +} + +func TestInstanceByURL(t *testing.T) { + testInstanceList(t, []string{"instance", "list", "-U", "http://admin:admin@127.0.0.1:4502", "-U", "http://admin:admin@127.0.0.1:4503", "-U", "test_author=http://admin:admin@127.0.0.1:4502"}, true, []string{"remote_adhoc_1", "remote_adhoc_2", "test_author"}) +} + +func TestConflictByIDAndAuthor(t *testing.T) { + testInstanceList(t, []string{"instance", "list", "-I", "local_author", "-A"}, false, []string{}) +} + +func TestConflictByIDAndLocal(t *testing.T) { + testInstanceList(t, []string{"instance", "list", "-I", "local_author", "-L"}, false, []string{}) +} + +func TestConflictByIDAndClassifier(t *testing.T) { + testInstanceList(t, []string{"instance", "list", "-I", "local_author", "-C", "dev"}, false, []string{}) +} + +func TestConflictIDAndURL(t *testing.T) { + testInstanceList(t, []string{"instance", "list", "-I", "local_author", "-U", "http://admin:admin@127.0.0.1:4502"}, false, []string{}) +} + +func TestConflictAuthorAndPublish(t *testing.T) { + testInstanceList(t, []string{"instance", "list", "-A", "-P"}, false, []string{}) +} + +func TestConflictAndLocalAndRemote(t *testing.T) { + testInstanceList(t, []string{"instance", "list", "-L", "-R"}, false, []string{}) +} diff --git a/pkg/instance_manager.go b/pkg/instance_manager.go index 3add1fce..75febab7 100644 --- a/pkg/instance_manager.go +++ b/pkg/instance_manager.go @@ -190,7 +190,7 @@ func (im *InstanceManager) filter(instances []Instance) []Instance { } } else { for _, i := range instances { - filterLocation := im.FilterAuthors && i.IsAuthor() || im.FilterPublishes && i.IsPublish() + filterLocation := im.FilterAuthors && i.IsAuthor() || im.FilterPublishes && i.IsPublish() || i.IsAdHoc() filterRole := im.FilterLocals && i.IsLocal() || im.FilterRemotes && i.IsRemote() filterClassifier := strings.HasPrefix(i.IDInfo().Classifier, im.FilterClassifierPrefix) if filterLocation && filterRole && filterClassifier { diff --git a/test/resources/aem.yml b/test/resources/aem.yml new file mode 100755 index 00000000..c797d863 --- /dev/null +++ b/test/resources/aem.yml @@ -0,0 +1,331 @@ +# AEM instances to work with +instance: + + # Full details of local or remote instances + config: + local_author: + active: [[.Env.AEM_AUTHOR_ACTIVE | default true ]] + http_url: [[.Env.AEM_AUTHOR_HTTP_URL | default "http://127.0.0.1:4502" ]] + user: [[.Env.AEM_AUTHOR_USER | default "admin" ]] + password: [[.Env.AEM_AUTHOR_PASSWORD | default "admin" ]] + run_modes: [ local ] + jvm_opts: + - -server + - -Djava.awt.headless=true + - -Djava.io.tmpdir=[[canonicalPath .Path "aem/home/tmp"]] + - -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=[[.Env.AEM_AUTHOR_DEBUG_ADDR | default "0.0.0.0:14502" ]] + - -Duser.language=en + - -Duser.country=US + - -Duser.timezone=UTC + start_opts: [] + secret_vars: + - ACME_SECRET=value + env_vars: + - ACME_VAR=value + sling_props: [] + local_publish: + active: [[.Env.AEM_PUBLISH_ACTIVE | default true ]] + http_url: [[.Env.AEM_PUBLISH_HTTP_URL | default "http://127.0.0.1:4503" ]] + user: [[.Env.AEM_PUBLISH_USER | default "admin" ]] + password: [[.Env.AEM_PUBLISH_PASSWORD | default "admin" ]] + run_modes: [ local ] + jvm_opts: + - -server + - -Djava.awt.headless=true + - -Djava.io.tmpdir=[[canonicalPath .Path "aem/home/tmp"]] + - -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=[[.Env.AEM_PUBLISH_DEBUG_ADDR | default "0.0.0.0:14503" ]] + - -Duser.language=en + - -Duser.country=US + - -Duser.timezone=UTC + start_opts: [] + secret_vars: + - ACME_SECRET=value + env_vars: + - ACME_VAR=value + sling_props: [] + remote_author_dev-auth: + http_url: http://192.168.0.1:4502 + user: admin + password: admin + remote_publish_dev-pub1: + http_url: http://192.168.0.2:4503 + user: admin + password: admin + remote_publish_dev-pub2: + http_url: http://192.168.0.3:4503 + user: admin + password: admin + + # Tuning performance & reliability + # 'auto' - for more than 1 local instances - 'serial', otherwise 'parallel' + # 'parallel' - for working with remote instances + # 'serial' - for working with local instances + processing_mode: auto + + # HTTP client settings + http: + timeout: 10m + debug: false + disable_warn: true + + # State checking + check: + # Time to wait before first state checking (to avoid false-positives) + warmup: 1s + # Time to wait for next state checking + interval: 6s + # Number of successful check attempts that indicates end of checking + done_threshold: 4 + # Wait only for those instances whose state has been changed internally (unaware of external changes) + await_strict: true + # Max time to wait for the instance to be healthy after executing the start script or e.g deploying a package + await_started: + timeout: 30m + # Max time to wait for the instance to be stopped after executing the stop script + await_stopped: + timeout: 10m + # Max time in which socket connection to instance should be established + reachable: + skip: true + timeout: 3s + # Bundle state tracking + bundle_stable: + skip: true + symbolic_names_ignored: [] + # OSGi events tracking + event_stable: + skip: true + # Topics indicating that instance is not stable + topics_unstable: + - "org/osgi/framework/ServiceEvent/*" + - "org/osgi/framework/FrameworkEvent/*" + - "org/osgi/framework/BundleEvent/*" + # Ignored service names to handle known issues + details_ignored: + - "*.*MBean" + - "org.osgi.service.component.runtime.ServiceComponentRuntime" + - "java.util.ResourceBundle" + received_max_age: 5s + # OSGi components state tracking + component_stable: + skip: true + pids: + include: ['[[.Env.AEM_PACKAGE | default "com.mysite" ]].*', 'com.day.crx.packaging.*', 'org.apache.sling.installer.*'] + exclude: ['org.apache.sling.installer.hc.*', 'org.apache.sling.installer.core.impl.console.*'] + match: + "disabled": [] + "no config": [] + "unsatisfied (reference)": [] + "satisfied": [] + # Sling Installer tracking + installer: + skip: true + # JMX state checking + state: true + # Pause Installation nodes checking + pause: true + # Specific endpoints / paths (like login page) + path_ready: + timeout: 10s + login_page: + skip: true + path: "/libs/granite/core/content/login.html" + status_code: 200 + contained_text: QUICKSTART_HOMEPAGE + + # Managed locally (set up automatically) + local: + # Current runtime dir (Sling launchpad, JCR repository) + unpack_dir: "aem/home/var/instance" + # Archived runtime dir (AEM backup files '*.aemb.zst') + backup_dir: "aem/home/var/backup" + + # Status discovery (timezone, AEM version, etc) + status: + timeout: 500ms + + # JCR Repository + repo: + property_change_ignored: + # AEM assigns them automatically + - "jcr:created" + - "cq:lastModified" + # AEM encrypts it right after changing by replication agent setup command + - "transportPassword" + + # CRX Package Manager + package: + # Force re-uploading/installing of snapshot AEM packages (just built / unreleased) + snapshot_patterns: [ "**/*-SNAPSHOT.zip" ] + snapshot_ignored: false + # Use checksums to avoid re-deployments when snapshot AEM packages are unchanged + snapshot_deploy_skipping: true + # Disable following workflow launchers for a package deployment time only + toggled_workflows: [/libs/settings/workflow/launcher/config/update_asset_*,/libs/settings/workflow/launcher/config/dam_*] + # Also sub-packages + install_recursive: true + # Use slower HTML endpoint for deployments but with better troubleshooting + install_html: + enabled: false + # Print HTML directly to console instead of writing to file + console: false + # Fail on case 'installed with errors' + strict: true + # Number of changes after which the commit to the repository is performed + install_save_threshold: 1024 + # Allows to relax dependency handling if needed + install_dependency_handling: required + # Controls how 'rep:policy' nodes are handled during import + install_ac_handling: '' + + # 'SSL By Default' + ssl: + setup_timeout: 30s + + # OSGi Framework + osgi: + shutdown_delay: 3s + + bundle: + install: + start: true + start_level: 20 + refresh_packages: true + + # Force re-uploading/installing of snapshot OSGi bundles (just built / unreleased) + snapshot_patterns: [ "**/*-SNAPSHOT.jar" ] + snapshot_ignored: false + # Use checksums to avoid re-installations when snapshot OSGi bundles are unchanged + snapshot_install_skipping: true + + # OAK Repository + oak: + index: + await_not_reindexed_timeout: 60m + + # Crypto Support + crypto: + key_bundle_symbolic_name: com.adobe.granite.crypto.file + + # Replication + replication: + bundle_symbolic_name: com.day.cq.cq-replication + + # Workflow Manager + workflow: + launcher: + lib_root: /libs/settings/workflow/launcher + config_root: /conf/global/settings/workflow/launcher + toggle_retry: + timeout: 10m + delay: 10s + +base: + # Location of library files (AEM SDK ZIP, Quickstart JAR & License, Crypto keys, service packs, additional packages, etc.) + lib_dir: aem/home/lib + # Location of temporary files (downloaded AEM packages, etc) + tmp_dir: aem/home/tmp + # Location of supportive tools (downloaded Java, OakRun, unpacked AEM SDK) + tool_dir: aem/home/opt + +log: + level: info + timestamp_format: "2006-01-02 15:04:05" + full_timestamp: true + +input: + format: yml + file: STDIN + +output: + format: text + log: + # File path of logs written especially when output format is different than 'text' + file: aem/home/var/log/aem.log + # Controls where outputs and logs should be written to when format is 'text' (console|file|both) + mode: console + +vendor: + # AEM instance source files + quickstart: + # AEM SDK ZIP or JAR + dist_file: 'aem/home/lib/{aem-sdk,cq-quickstart}-*.{zip,jar}' + # AEM License properties file + license_file: "aem/home/lib/license.properties" + + # JDK used to: run AEM instances, build OSGi bundles, assemble AEM packages + java: + # Require following versions before e.g running AEM instances + version_constraints: ">= 11, < 12" + + # Pre-installed local JDK dir + # a) keep it empty to download open source Java automatically for current OS and architecture + # b) set it to absolute path or to env var '[[.Env.JAVA_HOME]]' to indicate where closed source Java like Oracle is installed + home_dir: "" + + # Auto-installed JDK options + download: + # Source URL with template vars support + url: "https://github.com/adoptium/temurin11-binaries/releases/download/jdk-11.0.25%2B9/OpenJDK11U-jdk_[[.Arch]]_[[.Os]]_hotspot_11.0.25_9.[[.ArchiveExt]]" + # Map source URL template vars to be compatible with Adoptium Java + replacements: + # Var 'Os' (GOOS) + "darwin": "mac" + # Var 'Arch' (GOARCH) + "x86_64": "x64" + "amd64": "x64" + "386": "x86-32" + # enforce non-ARM Java as some AEM features are not working on ARM (e.g Scene7) + "arm64": "x64" + "aarch64": "x64" + + # Oak Run tool options (offline instance management) + oak_run: + download_url: "https://repo1.maven.org/maven2/org/apache/jackrabbit/oak-run/1.72.0/oak-run-1.72.0.jar" + store_path: "crx-quickstart/repository/segmentstore" + +# Content-related options +content: + clean: + # File patterns to be deleted + files_deleted: + - patterns: + - "**/.vlt" + - "**/.vlt*.tmp" + - "**/install/*.jar" + # File patterns to be flattened + files_flattened: + - "**/_cq_design_dialog/.content.xml" + - "**/_cq_dialog/.content.xml" + - "**/_cq_htmlTag/.content.xml" + - "**/_cq_template/.content.xml" + # Property patterns to be skipped, removed from cleaned file + properties_skipped: + - patterns: "jcr:uuid" + excluded_paths: [ "**/home/users/*", "**/home/groups/*" ] + - patterns: "cq:lastModified*" + excluded_paths: [ "**/content/experience-fragments/*" ] + - patterns: [ "dam:sha1", "dam:size" ] + included_paths: [ "**/content/dam/*.svg/*" ] + - patterns: + - "jcr:lastModified*" + - "jcr:created*" + - "jcr:isCheckedOut" + - "cq:lastReplicat*" + - "cq:lastRolledout*" + - "dam:extracted" + - "dam:assetState" + - "dc:modified" + - "*_x0040_*" + - "cq:name" + - "cq:parentPath" + - "dam:copiedAt" + - "dam:parentAssetID" + - "dam:relativePath" + # Mixin type patterns to be skipped, removed from cleaned file + mixin_types_skipped: + - patterns: + - "cq:ReplicationStatus" + - "mix:versionable" + # Unused namespaces to be skipped, removed from cleaned file + namespaces_skipped: true From 1873a06fc33725f3a51b99499493f80a0538626e Mon Sep 17 00:00:00 2001 From: Dominik Przybyl Date: Fri, 22 Nov 2024 11:32:15 +0100 Subject: [PATCH 4/9] refactor --- cmd/aem/instance_list_int_test.go | 60 ++---- cmd/aem/root.go | 15 +- pkg/instance_manager.go | 23 +-- test/resources/aem.yml | 331 ------------------------------ 4 files changed, 23 insertions(+), 406 deletions(-) delete mode 100755 test/resources/aem.yml diff --git a/cmd/aem/instance_list_int_test.go b/cmd/aem/instance_list_int_test.go index 4fad068b..e7385363 100644 --- a/cmd/aem/instance_list_int_test.go +++ b/cmd/aem/instance_list_int_test.go @@ -4,18 +4,12 @@ package main import ( "github.com/wttech/aemc/pkg" - "github.com/wttech/aemc/pkg/cfg" - "os" "sort" "strings" "testing" ) func testInstanceList(t *testing.T, args []string, expectedResult bool, expectedIDs []string) { - if err := os.Setenv(cfg.FileEnvVar, "../../test/resources/aem.yml"); err != nil { - t.Fatal(err) - } - cli := NewCLI() cmd := cli.cmd cmd.SetArgs(args) @@ -51,69 +45,45 @@ func testInstanceList(t *testing.T, args []string, expectedResult bool, expected } func TestAllInstances(t *testing.T) { - testInstanceList(t, []string{"instance", "list"}, true, []string{"local_author", "local_publish", "remote_author_dev-auth", "remote_publish_dev-pub1", "remote_publish_dev-pub2"}) + testInstanceList(t, []string{"instance", "list", "--output-value", "NONE"}, true, []string{"local_author", "local_publish"}) } func TestAuthorInstances(t *testing.T) { - testInstanceList(t, []string{"instance", "list", "-A"}, true, []string{"local_author", "remote_author_dev-auth"}) + testInstanceList(t, []string{"instance", "list", "-A", "--output-value", "NONE"}, true, []string{"local_author"}) } func TestPublishInstances(t *testing.T) { - testInstanceList(t, []string{"instance", "list", "-P"}, true, []string{"local_publish", "remote_publish_dev-pub1", "remote_publish_dev-pub2"}) -} - -func TestLocalInstances(t *testing.T) { - testInstanceList(t, []string{"instance", "list", "-L"}, true, []string{"local_author", "local_publish"}) + testInstanceList(t, []string{"instance", "list", "-P", "--output-value", "NONE"}, true, []string{"local_publish"}) } -func TestLocalAuthorInstances(t *testing.T) { - testInstanceList(t, []string{"instance", "list", "-L", "-A"}, true, []string{"local_author"}) -} - -func TestRemoteInstances(t *testing.T) { - testInstanceList(t, []string{"instance", "list", "-R"}, true, []string{"remote_author_dev-auth", "remote_publish_dev-pub1", "remote_publish_dev-pub2"}) -} - -func TestRemotePublishInstances(t *testing.T) { - testInstanceList(t, []string{"instance", "list", "-R", "-P"}, true, []string{"remote_publish_dev-pub1", "remote_publish_dev-pub2"}) +func TestInstanceByID(t *testing.T) { + testInstanceList(t, []string{"instance", "list", "-I", "local_author", "-I", "local_publish", "--output-value", "NONE"}, true, []string{"local_author", "local_publish"}) } -func TestDevInstances(t *testing.T) { - testInstanceList(t, []string{"instance", "list", "-C", "dev"}, true, []string{"remote_author_dev-auth", "remote_publish_dev-pub1", "remote_publish_dev-pub2"}) +func TestInstanceByURL(t *testing.T) { + testInstanceList(t, []string{"instance", "list", "-U", "http://admin:admin@127.0.0.1:4502", "-U", "http://admin:admin@127.0.0.1:4503", "-U", "test_author=http://admin:admin@127.0.0.1:4502", "--output-value", "NONE"}, true, []string{"remote_adhoc_1", "remote_adhoc_2", "test_author"}) } -func TestPublishDevInstances(t *testing.T) { - testInstanceList(t, []string{"instance", "list", "-P", "-C", "dev"}, true, []string{"remote_publish_dev-pub1", "remote_publish_dev-pub2"}) +func TestAuthorInstanceByURL(t *testing.T) { + testInstanceList(t, []string{"instance", "list", "-U", "dev-auth_author=http://admin:admin@127.0.0.1:4502", "-U", "dev-pub1_publish=http://admin:admin@127.0.0.1:4503", "-U", "dev-pub2_publish=http://admin:admin@127.0.0.1:4504", "-A", "--output-value", "NONE"}, true, []string{"dev-auth_author"}) } -func TestInstanceByID(t *testing.T) { - testInstanceList(t, []string{"instance", "list", "-I", "local_author", "-I", "remote_publish_dev-pub2"}, true, []string{"local_author", "remote_publish_dev-pub2"}) +func TestPublishInstanceByURL(t *testing.T) { + testInstanceList(t, []string{"instance", "list", "-U", "dev-auth_author=http://admin:admin@127.0.0.1:4502", "-U", "dev-pub1_publish=http://admin:admin@127.0.0.1:4503", "-U", "dev-pub2_publish=http://admin:admin@127.0.0.1:4504", "-P", "--output-value", "NONE"}, true, []string{"dev-pub1_publish", "dev-pub2_publish"}) } -func TestInstanceByURL(t *testing.T) { - testInstanceList(t, []string{"instance", "list", "-U", "http://admin:admin@127.0.0.1:4502", "-U", "http://admin:admin@127.0.0.1:4503", "-U", "test_author=http://admin:admin@127.0.0.1:4502"}, true, []string{"remote_adhoc_1", "remote_adhoc_2", "test_author"}) +func TestConflictAuthorAndPublish(t *testing.T) { + testInstanceList(t, []string{"instance", "list", "-A", "-P"}, false, []string{}) } func TestConflictByIDAndAuthor(t *testing.T) { testInstanceList(t, []string{"instance", "list", "-I", "local_author", "-A"}, false, []string{}) } -func TestConflictByIDAndLocal(t *testing.T) { - testInstanceList(t, []string{"instance", "list", "-I", "local_author", "-L"}, false, []string{}) -} - -func TestConflictByIDAndClassifier(t *testing.T) { - testInstanceList(t, []string{"instance", "list", "-I", "local_author", "-C", "dev"}, false, []string{}) +func TestConflictByIDAndPublish(t *testing.T) { + testInstanceList(t, []string{"instance", "list", "-I", "local_author", "-P"}, false, []string{}) } func TestConflictIDAndURL(t *testing.T) { testInstanceList(t, []string{"instance", "list", "-I", "local_author", "-U", "http://admin:admin@127.0.0.1:4502"}, false, []string{}) } - -func TestConflictAuthorAndPublish(t *testing.T) { - testInstanceList(t, []string{"instance", "list", "-A", "-P"}, false, []string{}) -} - -func TestConflictAndLocalAndRemote(t *testing.T) { - testInstanceList(t, []string{"instance", "list", "-L", "-R"}, false, []string{}) -} diff --git a/cmd/aem/root.go b/cmd/aem/root.go index 1a29a7af..802a3a7b 100644 --- a/cmd/aem/root.go +++ b/cmd/aem/root.go @@ -80,18 +80,9 @@ func (c *CLI) rootFlags(cmd *cobra.Command) { cmd.PersistentFlags().BoolP("instance-publish", "P", cv.GetBool("instance.filter.publishes"), "Use only AEM publish instance(s)") _ = cv.BindPFlag("instance.filter.publishes", cmd.PersistentFlags().Lookup("instance-publish")) - cmd.PersistentFlags().BoolP("instance-local", "L", cv.GetBool("instance.filter.locals"), "Use only AEM local instance(s)") - _ = cv.BindPFlag("instance.filter.locals", cmd.PersistentFlags().Lookup("instance-local")) - - cmd.PersistentFlags().BoolP("instance-remote", "R", cv.GetBool("instance.filter.remotes"), "Use only AEM remote instance(s)") - _ = cv.BindPFlag("instance.filter.remotes", cmd.PersistentFlags().Lookup("instance-remote")) - - cmd.PersistentFlags().StringP("instance-classifier-prefix", "C", cv.GetString("instance.filter.classifier-prefix"), "Use only AEM instance(s) with proper classifier prefix") - _ = cv.BindPFlag("instance.filter.classifier-prefix", cmd.PersistentFlags().Lookup("instance-classifier-prefix")) - - cmd.MarkFlagsMutuallyExclusive("instance-url", "instance-id", "instance-author", "instance-publish") - cmd.MarkFlagsMutuallyExclusive("instance-url", "instance-id", "instance-local", "instance-remote") - cmd.MarkFlagsMutuallyExclusive("instance-url", "instance-id", "instance-classifier-prefix") + cmd.MarkFlagsMutuallyExclusive("instance-url", "instance-id") + cmd.MarkFlagsMutuallyExclusive("instance-author", "instance-publish") + cmd.MarkFlagsMutuallyExclusive("instance-id", "instance-author", "instance-publish") cmd.PersistentFlags().String("instance-processing", cv.GetString("instance.processing_mode"), "Controls processing mode for instances ("+(strings.Join(instance.ProcessingModes(), "|")+")")) _ = cv.BindPFlag("instance.processing_mode", cmd.PersistentFlags().Lookup("instance-processing")) diff --git a/pkg/instance_manager.go b/pkg/instance_manager.go index 75febab7..571cd4f9 100644 --- a/pkg/instance_manager.go +++ b/pkg/instance_manager.go @@ -21,13 +21,10 @@ type InstanceManager struct { AdHocURLs []string - FilterIDs []string - FilterAuthors bool - FilterPublishes bool - FilterLocals bool - FilterRemotes bool - FilterClassifierPrefix string - ProcessingMode string + FilterIDs []string + FilterAuthors bool + FilterPublishes bool + ProcessingMode string } func NewInstanceManager(aem *AEM) *InstanceManager { @@ -45,13 +42,6 @@ func NewInstanceManager(aem *AEM) *InstanceManager { result.FilterAuthors = true result.FilterPublishes = true } - result.FilterLocals = cv.GetBool("instance.filter.locals") - result.FilterRemotes = cv.GetBool("instance.filter.remotes") - if !result.FilterLocals && !result.FilterRemotes { - result.FilterLocals = true - result.FilterRemotes = true - } - result.FilterClassifierPrefix = cv.GetString("instance.filter.classifier-prefix") result.ProcessingMode = cv.GetString("instance.processing_mode") result.LocalOpts = NewLocalOpts(result) @@ -190,10 +180,7 @@ func (im *InstanceManager) filter(instances []Instance) []Instance { } } else { for _, i := range instances { - filterLocation := im.FilterAuthors && i.IsAuthor() || im.FilterPublishes && i.IsPublish() || i.IsAdHoc() - filterRole := im.FilterLocals && i.IsLocal() || im.FilterRemotes && i.IsRemote() - filterClassifier := strings.HasPrefix(i.IDInfo().Classifier, im.FilterClassifierPrefix) - if filterLocation && filterRole && filterClassifier { + if im.FilterAuthors && i.IsAuthor() || im.FilterPublishes && i.IsPublish() || i.IsAdHoc() { result = append(result, i) } } diff --git a/test/resources/aem.yml b/test/resources/aem.yml deleted file mode 100755 index c797d863..00000000 --- a/test/resources/aem.yml +++ /dev/null @@ -1,331 +0,0 @@ -# AEM instances to work with -instance: - - # Full details of local or remote instances - config: - local_author: - active: [[.Env.AEM_AUTHOR_ACTIVE | default true ]] - http_url: [[.Env.AEM_AUTHOR_HTTP_URL | default "http://127.0.0.1:4502" ]] - user: [[.Env.AEM_AUTHOR_USER | default "admin" ]] - password: [[.Env.AEM_AUTHOR_PASSWORD | default "admin" ]] - run_modes: [ local ] - jvm_opts: - - -server - - -Djava.awt.headless=true - - -Djava.io.tmpdir=[[canonicalPath .Path "aem/home/tmp"]] - - -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=[[.Env.AEM_AUTHOR_DEBUG_ADDR | default "0.0.0.0:14502" ]] - - -Duser.language=en - - -Duser.country=US - - -Duser.timezone=UTC - start_opts: [] - secret_vars: - - ACME_SECRET=value - env_vars: - - ACME_VAR=value - sling_props: [] - local_publish: - active: [[.Env.AEM_PUBLISH_ACTIVE | default true ]] - http_url: [[.Env.AEM_PUBLISH_HTTP_URL | default "http://127.0.0.1:4503" ]] - user: [[.Env.AEM_PUBLISH_USER | default "admin" ]] - password: [[.Env.AEM_PUBLISH_PASSWORD | default "admin" ]] - run_modes: [ local ] - jvm_opts: - - -server - - -Djava.awt.headless=true - - -Djava.io.tmpdir=[[canonicalPath .Path "aem/home/tmp"]] - - -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=[[.Env.AEM_PUBLISH_DEBUG_ADDR | default "0.0.0.0:14503" ]] - - -Duser.language=en - - -Duser.country=US - - -Duser.timezone=UTC - start_opts: [] - secret_vars: - - ACME_SECRET=value - env_vars: - - ACME_VAR=value - sling_props: [] - remote_author_dev-auth: - http_url: http://192.168.0.1:4502 - user: admin - password: admin - remote_publish_dev-pub1: - http_url: http://192.168.0.2:4503 - user: admin - password: admin - remote_publish_dev-pub2: - http_url: http://192.168.0.3:4503 - user: admin - password: admin - - # Tuning performance & reliability - # 'auto' - for more than 1 local instances - 'serial', otherwise 'parallel' - # 'parallel' - for working with remote instances - # 'serial' - for working with local instances - processing_mode: auto - - # HTTP client settings - http: - timeout: 10m - debug: false - disable_warn: true - - # State checking - check: - # Time to wait before first state checking (to avoid false-positives) - warmup: 1s - # Time to wait for next state checking - interval: 6s - # Number of successful check attempts that indicates end of checking - done_threshold: 4 - # Wait only for those instances whose state has been changed internally (unaware of external changes) - await_strict: true - # Max time to wait for the instance to be healthy after executing the start script or e.g deploying a package - await_started: - timeout: 30m - # Max time to wait for the instance to be stopped after executing the stop script - await_stopped: - timeout: 10m - # Max time in which socket connection to instance should be established - reachable: - skip: true - timeout: 3s - # Bundle state tracking - bundle_stable: - skip: true - symbolic_names_ignored: [] - # OSGi events tracking - event_stable: - skip: true - # Topics indicating that instance is not stable - topics_unstable: - - "org/osgi/framework/ServiceEvent/*" - - "org/osgi/framework/FrameworkEvent/*" - - "org/osgi/framework/BundleEvent/*" - # Ignored service names to handle known issues - details_ignored: - - "*.*MBean" - - "org.osgi.service.component.runtime.ServiceComponentRuntime" - - "java.util.ResourceBundle" - received_max_age: 5s - # OSGi components state tracking - component_stable: - skip: true - pids: - include: ['[[.Env.AEM_PACKAGE | default "com.mysite" ]].*', 'com.day.crx.packaging.*', 'org.apache.sling.installer.*'] - exclude: ['org.apache.sling.installer.hc.*', 'org.apache.sling.installer.core.impl.console.*'] - match: - "disabled": [] - "no config": [] - "unsatisfied (reference)": [] - "satisfied": [] - # Sling Installer tracking - installer: - skip: true - # JMX state checking - state: true - # Pause Installation nodes checking - pause: true - # Specific endpoints / paths (like login page) - path_ready: - timeout: 10s - login_page: - skip: true - path: "/libs/granite/core/content/login.html" - status_code: 200 - contained_text: QUICKSTART_HOMEPAGE - - # Managed locally (set up automatically) - local: - # Current runtime dir (Sling launchpad, JCR repository) - unpack_dir: "aem/home/var/instance" - # Archived runtime dir (AEM backup files '*.aemb.zst') - backup_dir: "aem/home/var/backup" - - # Status discovery (timezone, AEM version, etc) - status: - timeout: 500ms - - # JCR Repository - repo: - property_change_ignored: - # AEM assigns them automatically - - "jcr:created" - - "cq:lastModified" - # AEM encrypts it right after changing by replication agent setup command - - "transportPassword" - - # CRX Package Manager - package: - # Force re-uploading/installing of snapshot AEM packages (just built / unreleased) - snapshot_patterns: [ "**/*-SNAPSHOT.zip" ] - snapshot_ignored: false - # Use checksums to avoid re-deployments when snapshot AEM packages are unchanged - snapshot_deploy_skipping: true - # Disable following workflow launchers for a package deployment time only - toggled_workflows: [/libs/settings/workflow/launcher/config/update_asset_*,/libs/settings/workflow/launcher/config/dam_*] - # Also sub-packages - install_recursive: true - # Use slower HTML endpoint for deployments but with better troubleshooting - install_html: - enabled: false - # Print HTML directly to console instead of writing to file - console: false - # Fail on case 'installed with errors' - strict: true - # Number of changes after which the commit to the repository is performed - install_save_threshold: 1024 - # Allows to relax dependency handling if needed - install_dependency_handling: required - # Controls how 'rep:policy' nodes are handled during import - install_ac_handling: '' - - # 'SSL By Default' - ssl: - setup_timeout: 30s - - # OSGi Framework - osgi: - shutdown_delay: 3s - - bundle: - install: - start: true - start_level: 20 - refresh_packages: true - - # Force re-uploading/installing of snapshot OSGi bundles (just built / unreleased) - snapshot_patterns: [ "**/*-SNAPSHOT.jar" ] - snapshot_ignored: false - # Use checksums to avoid re-installations when snapshot OSGi bundles are unchanged - snapshot_install_skipping: true - - # OAK Repository - oak: - index: - await_not_reindexed_timeout: 60m - - # Crypto Support - crypto: - key_bundle_symbolic_name: com.adobe.granite.crypto.file - - # Replication - replication: - bundle_symbolic_name: com.day.cq.cq-replication - - # Workflow Manager - workflow: - launcher: - lib_root: /libs/settings/workflow/launcher - config_root: /conf/global/settings/workflow/launcher - toggle_retry: - timeout: 10m - delay: 10s - -base: - # Location of library files (AEM SDK ZIP, Quickstart JAR & License, Crypto keys, service packs, additional packages, etc.) - lib_dir: aem/home/lib - # Location of temporary files (downloaded AEM packages, etc) - tmp_dir: aem/home/tmp - # Location of supportive tools (downloaded Java, OakRun, unpacked AEM SDK) - tool_dir: aem/home/opt - -log: - level: info - timestamp_format: "2006-01-02 15:04:05" - full_timestamp: true - -input: - format: yml - file: STDIN - -output: - format: text - log: - # File path of logs written especially when output format is different than 'text' - file: aem/home/var/log/aem.log - # Controls where outputs and logs should be written to when format is 'text' (console|file|both) - mode: console - -vendor: - # AEM instance source files - quickstart: - # AEM SDK ZIP or JAR - dist_file: 'aem/home/lib/{aem-sdk,cq-quickstart}-*.{zip,jar}' - # AEM License properties file - license_file: "aem/home/lib/license.properties" - - # JDK used to: run AEM instances, build OSGi bundles, assemble AEM packages - java: - # Require following versions before e.g running AEM instances - version_constraints: ">= 11, < 12" - - # Pre-installed local JDK dir - # a) keep it empty to download open source Java automatically for current OS and architecture - # b) set it to absolute path or to env var '[[.Env.JAVA_HOME]]' to indicate where closed source Java like Oracle is installed - home_dir: "" - - # Auto-installed JDK options - download: - # Source URL with template vars support - url: "https://github.com/adoptium/temurin11-binaries/releases/download/jdk-11.0.25%2B9/OpenJDK11U-jdk_[[.Arch]]_[[.Os]]_hotspot_11.0.25_9.[[.ArchiveExt]]" - # Map source URL template vars to be compatible with Adoptium Java - replacements: - # Var 'Os' (GOOS) - "darwin": "mac" - # Var 'Arch' (GOARCH) - "x86_64": "x64" - "amd64": "x64" - "386": "x86-32" - # enforce non-ARM Java as some AEM features are not working on ARM (e.g Scene7) - "arm64": "x64" - "aarch64": "x64" - - # Oak Run tool options (offline instance management) - oak_run: - download_url: "https://repo1.maven.org/maven2/org/apache/jackrabbit/oak-run/1.72.0/oak-run-1.72.0.jar" - store_path: "crx-quickstart/repository/segmentstore" - -# Content-related options -content: - clean: - # File patterns to be deleted - files_deleted: - - patterns: - - "**/.vlt" - - "**/.vlt*.tmp" - - "**/install/*.jar" - # File patterns to be flattened - files_flattened: - - "**/_cq_design_dialog/.content.xml" - - "**/_cq_dialog/.content.xml" - - "**/_cq_htmlTag/.content.xml" - - "**/_cq_template/.content.xml" - # Property patterns to be skipped, removed from cleaned file - properties_skipped: - - patterns: "jcr:uuid" - excluded_paths: [ "**/home/users/*", "**/home/groups/*" ] - - patterns: "cq:lastModified*" - excluded_paths: [ "**/content/experience-fragments/*" ] - - patterns: [ "dam:sha1", "dam:size" ] - included_paths: [ "**/content/dam/*.svg/*" ] - - patterns: - - "jcr:lastModified*" - - "jcr:created*" - - "jcr:isCheckedOut" - - "cq:lastReplicat*" - - "cq:lastRolledout*" - - "dam:extracted" - - "dam:assetState" - - "dc:modified" - - "*_x0040_*" - - "cq:name" - - "cq:parentPath" - - "dam:copiedAt" - - "dam:parentAssetID" - - "dam:relativePath" - # Mixin type patterns to be skipped, removed from cleaned file - mixin_types_skipped: - - patterns: - - "cq:ReplicationStatus" - - "mix:versionable" - # Unused namespaces to be skipped, removed from cleaned file - namespaces_skipped: true From 97410cbd2e440278f4b2644b673850d5dae35bc6 Mon Sep 17 00:00:00 2001 From: Dominik Przybyl Date: Fri, 22 Nov 2024 11:51:29 +0100 Subject: [PATCH 5/9] minor --- cmd/aem/root.go | 1 - 1 file changed, 1 deletion(-) diff --git a/cmd/aem/root.go b/cmd/aem/root.go index 802a3a7b..13519bc0 100644 --- a/cmd/aem/root.go +++ b/cmd/aem/root.go @@ -81,7 +81,6 @@ func (c *CLI) rootFlags(cmd *cobra.Command) { _ = cv.BindPFlag("instance.filter.publishes", cmd.PersistentFlags().Lookup("instance-publish")) cmd.MarkFlagsMutuallyExclusive("instance-url", "instance-id") - cmd.MarkFlagsMutuallyExclusive("instance-author", "instance-publish") cmd.MarkFlagsMutuallyExclusive("instance-id", "instance-author", "instance-publish") cmd.PersistentFlags().String("instance-processing", cv.GetString("instance.processing_mode"), "Controls processing mode for instances ("+(strings.Join(instance.ProcessingModes(), "|")+")")) From db1f2ba2502323f0bfe4a68526298d8e399e441d Mon Sep 17 00:00:00 2001 From: Dominik Przybyl Date: Fri, 22 Nov 2024 11:56:18 +0100 Subject: [PATCH 6/9] minor --- pkg/instance_manager.go | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/pkg/instance_manager.go b/pkg/instance_manager.go index 571cd4f9..adaaf8fd 100644 --- a/pkg/instance_manager.go +++ b/pkg/instance_manager.go @@ -38,10 +38,6 @@ func NewInstanceManager(aem *AEM) *InstanceManager { result.FilterIDs = cv.GetStringSlice("instance.filter.id") result.FilterAuthors = cv.GetBool("instance.filter.authors") result.FilterPublishes = cv.GetBool("instance.filter.publishes") - if !result.FilterAuthors && !result.FilterPublishes { - result.FilterAuthors = true - result.FilterPublishes = true - } result.ProcessingMode = cv.GetString("instance.processing_mode") result.LocalOpts = NewLocalOpts(result) @@ -180,7 +176,7 @@ func (im *InstanceManager) filter(instances []Instance) []Instance { } } else { for _, i := range instances { - if im.FilterAuthors && i.IsAuthor() || im.FilterPublishes && i.IsPublish() || i.IsAdHoc() { + if im.FilterAuthors == im.FilterPublishes || im.FilterAuthors && i.IsAuthor() || im.FilterPublishes && i.IsPublish() { result = append(result, i) } } From 30842ef988a2e88f6540179cf9446a56c81f1365 Mon Sep 17 00:00:00 2001 From: Dominik Przybyl Date: Fri, 22 Nov 2024 20:47:02 +0100 Subject: [PATCH 7/9] refactor --- cmd/aem/instance_list_int_test.go | 22 ++++------------- cmd/aem/root.go | 3 --- pkg/instance_manager.go | 41 ++++++++++++++++--------------- 3 files changed, 26 insertions(+), 40 deletions(-) diff --git a/cmd/aem/instance_list_int_test.go b/cmd/aem/instance_list_int_test.go index e7385363..b5cf3098 100644 --- a/cmd/aem/instance_list_int_test.go +++ b/cmd/aem/instance_list_int_test.go @@ -57,13 +57,17 @@ func TestPublishInstances(t *testing.T) { } func TestInstanceByID(t *testing.T) { - testInstanceList(t, []string{"instance", "list", "-I", "local_author", "-I", "local_publish", "--output-value", "NONE"}, true, []string{"local_author", "local_publish"}) + testInstanceList(t, []string{"instance", "list", "-I", "local_author", "--output-value", "NONE"}, true, []string{"local_author"}) } func TestInstanceByURL(t *testing.T) { testInstanceList(t, []string{"instance", "list", "-U", "http://admin:admin@127.0.0.1:4502", "-U", "http://admin:admin@127.0.0.1:4503", "-U", "test_author=http://admin:admin@127.0.0.1:4502", "--output-value", "NONE"}, true, []string{"remote_adhoc_1", "remote_adhoc_2", "test_author"}) } +func TestInstanceByIDOrURL(t *testing.T) { + testInstanceList(t, []string{"instance", "list", "-I", "local_publish", "-U", "http://admin:admin@127.0.0.1:4502", "--output-value", "NONE"}, true, []string{"local_publish", "remote_adhoc"}) +} + func TestAuthorInstanceByURL(t *testing.T) { testInstanceList(t, []string{"instance", "list", "-U", "dev-auth_author=http://admin:admin@127.0.0.1:4502", "-U", "dev-pub1_publish=http://admin:admin@127.0.0.1:4503", "-U", "dev-pub2_publish=http://admin:admin@127.0.0.1:4504", "-A", "--output-value", "NONE"}, true, []string{"dev-auth_author"}) } @@ -71,19 +75,3 @@ func TestAuthorInstanceByURL(t *testing.T) { func TestPublishInstanceByURL(t *testing.T) { testInstanceList(t, []string{"instance", "list", "-U", "dev-auth_author=http://admin:admin@127.0.0.1:4502", "-U", "dev-pub1_publish=http://admin:admin@127.0.0.1:4503", "-U", "dev-pub2_publish=http://admin:admin@127.0.0.1:4504", "-P", "--output-value", "NONE"}, true, []string{"dev-pub1_publish", "dev-pub2_publish"}) } - -func TestConflictAuthorAndPublish(t *testing.T) { - testInstanceList(t, []string{"instance", "list", "-A", "-P"}, false, []string{}) -} - -func TestConflictByIDAndAuthor(t *testing.T) { - testInstanceList(t, []string{"instance", "list", "-I", "local_author", "-A"}, false, []string{}) -} - -func TestConflictByIDAndPublish(t *testing.T) { - testInstanceList(t, []string{"instance", "list", "-I", "local_author", "-P"}, false, []string{}) -} - -func TestConflictIDAndURL(t *testing.T) { - testInstanceList(t, []string{"instance", "list", "-I", "local_author", "-U", "http://admin:admin@127.0.0.1:4502"}, false, []string{}) -} diff --git a/cmd/aem/root.go b/cmd/aem/root.go index 13519bc0..107fdcf0 100644 --- a/cmd/aem/root.go +++ b/cmd/aem/root.go @@ -80,9 +80,6 @@ func (c *CLI) rootFlags(cmd *cobra.Command) { cmd.PersistentFlags().BoolP("instance-publish", "P", cv.GetBool("instance.filter.publishes"), "Use only AEM publish instance(s)") _ = cv.BindPFlag("instance.filter.publishes", cmd.PersistentFlags().Lookup("instance-publish")) - cmd.MarkFlagsMutuallyExclusive("instance-url", "instance-id") - cmd.MarkFlagsMutuallyExclusive("instance-id", "instance-author", "instance-publish") - cmd.PersistentFlags().String("instance-processing", cv.GetString("instance.processing_mode"), "Controls processing mode for instances ("+(strings.Join(instance.ProcessingModes(), "|")+")")) _ = cv.BindPFlag("instance.processing_mode", cmd.PersistentFlags().Lookup("instance-processing")) } diff --git a/pkg/instance_manager.go b/pkg/instance_manager.go index adaaf8fd..515982cf 100644 --- a/pkg/instance_manager.go +++ b/pkg/instance_manager.go @@ -84,8 +84,8 @@ func (im *InstanceManager) All() []Instance { } func (im *InstanceManager) newAdHocOrFromConfig() []Instance { + var result []Instance if len(im.AdHocURLs) > 0 { - var result []Instance for adHocIndex, adHocValue := range im.AdHocURLs { iURL, err := im.newAdhoc(adHocValue, adHocIndex, len(im.AdHocURLs)) if err != nil { @@ -93,24 +93,36 @@ func (im *InstanceManager) newAdHocOrFromConfig() []Instance { } result = append(result, *iURL) } - return result } + var instances []Instance cv := im.aem.config.Values() configIDs := maps.Keys(cv.GetStringMap("instance.config")) if len(configIDs) > 0 { - var result []Instance for _, id := range configIDs { cv.SetDefault(fmt.Sprintf("instance.config.%s.active", id), true) active := cv.GetBool(fmt.Sprintf("instance.config.%s.active", id)) if active { if i := im.newFromConfig(id); i != nil { - result = append(result, *i) + instances = append(instances, *i) } } } - return result + } else { + instances = im.NewLocalPair() + } + if len(im.FilterIDs) > 0 { + for _, i := range instances { + for _, filterID := range im.FilterIDs { + if i.id == filterID { + result = append(result, i) + break + } + } + } + } else if len(im.AdHocURLs) == 0 { + result = instances } - return im.NewLocalPair() + return result } func (im *InstanceManager) newAdhoc(value string, current int, total int) (*Instance, error) { @@ -165,20 +177,9 @@ func (im *InstanceManager) newFromConfig(id string) *Instance { func (im *InstanceManager) filter(instances []Instance) []Instance { result := []Instance{} - if len(im.FilterIDs) > 0 { - for _, i := range instances { - for _, filterID := range im.FilterIDs { - if i.id == filterID { - result = append(result, i) - break - } - } - } - } else { - for _, i := range instances { - if im.FilterAuthors == im.FilterPublishes || im.FilterAuthors && i.IsAuthor() || im.FilterPublishes && i.IsPublish() { - result = append(result, i) - } + for _, i := range instances { + if im.FilterAuthors == im.FilterPublishes || im.FilterAuthors && i.IsAuthor() || im.FilterPublishes && i.IsPublish() { + result = append(result, i) } } sort.SliceStable(result, func(i, j int) bool { From da3a67493e3dacb3335fac86acc378d558cb25d3 Mon Sep 17 00:00:00 2001 From: Dominik Przybyl Date: Fri, 22 Nov 2024 21:17:57 +0100 Subject: [PATCH 8/9] test refactor --- cmd/aem/instance_list_int_test.go | 52 ++++++++++++++----------------- 1 file changed, 24 insertions(+), 28 deletions(-) diff --git a/cmd/aem/instance_list_int_test.go b/cmd/aem/instance_list_int_test.go index b5cf3098..f26a98e4 100644 --- a/cmd/aem/instance_list_int_test.go +++ b/cmd/aem/instance_list_int_test.go @@ -9,35 +9,31 @@ import ( "testing" ) -func testInstanceList(t *testing.T, args []string, expectedResult bool, expectedIDs []string) { +func testInstanceList(t *testing.T, args []string, expectedIDs []string) { cli := NewCLI() cmd := cli.cmd cmd.SetArgs(args) defer func() { - err := recover() - actualResult := err != nil - if actualResult != expectedResult { - t.Errorf("InstanceList(%v) = %v; want %v", args, actualResult, expectedResult) - } else if actualResult && expectedResult { - actualIDs := []string{} + actualIDs := []string{} + if recover() != nil { instances := cli.outputResponse.Data["instances"].([]pkg.Instance) for _, i := range instances { actualIDs = append(actualIDs, i.ID()) } - sort.SliceStable(actualIDs, func(i, j int) bool { - return strings.Compare(actualIDs[i], actualIDs[j]) < 0 - }) - sort.SliceStable(expectedIDs, func(i, j int) bool { - return strings.Compare(expectedIDs[i], expectedIDs[j]) < 0 - }) - result := len(actualIDs) == len(expectedIDs) - for i := range actualIDs { - result = result && actualIDs[i] == expectedIDs[i] - } - if !result { - t.Errorf("InstanceList(%v) = %v; want %v", args, actualIDs, expectedIDs) - } + } + sort.SliceStable(actualIDs, func(i, j int) bool { + return strings.Compare(actualIDs[i], actualIDs[j]) < 0 + }) + sort.SliceStable(expectedIDs, func(i, j int) bool { + return strings.Compare(expectedIDs[i], expectedIDs[j]) < 0 + }) + result := len(actualIDs) == len(expectedIDs) + for i := range actualIDs { + result = result && actualIDs[i] == expectedIDs[i] + } + if !result { + t.Errorf("InstanceList(%v) = %v; want %v", args, actualIDs, expectedIDs) } }() @@ -45,33 +41,33 @@ func testInstanceList(t *testing.T, args []string, expectedResult bool, expected } func TestAllInstances(t *testing.T) { - testInstanceList(t, []string{"instance", "list", "--output-value", "NONE"}, true, []string{"local_author", "local_publish"}) + testInstanceList(t, []string{"instance", "list", "--output-value", "NONE"}, []string{"local_author", "local_publish"}) } func TestAuthorInstances(t *testing.T) { - testInstanceList(t, []string{"instance", "list", "-A", "--output-value", "NONE"}, true, []string{"local_author"}) + testInstanceList(t, []string{"instance", "list", "-A", "--output-value", "NONE"}, []string{"local_author"}) } func TestPublishInstances(t *testing.T) { - testInstanceList(t, []string{"instance", "list", "-P", "--output-value", "NONE"}, true, []string{"local_publish"}) + testInstanceList(t, []string{"instance", "list", "-P", "--output-value", "NONE"}, []string{"local_publish"}) } func TestInstanceByID(t *testing.T) { - testInstanceList(t, []string{"instance", "list", "-I", "local_author", "--output-value", "NONE"}, true, []string{"local_author"}) + testInstanceList(t, []string{"instance", "list", "-I", "local_author", "--output-value", "NONE"}, []string{"local_author"}) } func TestInstanceByURL(t *testing.T) { - testInstanceList(t, []string{"instance", "list", "-U", "http://admin:admin@127.0.0.1:4502", "-U", "http://admin:admin@127.0.0.1:4503", "-U", "test_author=http://admin:admin@127.0.0.1:4502", "--output-value", "NONE"}, true, []string{"remote_adhoc_1", "remote_adhoc_2", "test_author"}) + testInstanceList(t, []string{"instance", "list", "-U", "http://admin:admin@127.0.0.1:4502", "-U", "http://admin:admin@127.0.0.1:4503", "-U", "test_author=http://admin:admin@127.0.0.1:4502", "--output-value", "NONE"}, []string{"remote_adhoc_1", "remote_adhoc_2", "test_author"}) } func TestInstanceByIDOrURL(t *testing.T) { - testInstanceList(t, []string{"instance", "list", "-I", "local_publish", "-U", "http://admin:admin@127.0.0.1:4502", "--output-value", "NONE"}, true, []string{"local_publish", "remote_adhoc"}) + testInstanceList(t, []string{"instance", "list", "-I", "local_publish", "-U", "http://admin:admin@127.0.0.1:4502", "--output-value", "NONE"}, []string{"local_publish", "remote_adhoc"}) } func TestAuthorInstanceByURL(t *testing.T) { - testInstanceList(t, []string{"instance", "list", "-U", "dev-auth_author=http://admin:admin@127.0.0.1:4502", "-U", "dev-pub1_publish=http://admin:admin@127.0.0.1:4503", "-U", "dev-pub2_publish=http://admin:admin@127.0.0.1:4504", "-A", "--output-value", "NONE"}, true, []string{"dev-auth_author"}) + testInstanceList(t, []string{"instance", "list", "-U", "dev-auth_author=http://admin:admin@127.0.0.1:4502", "-U", "dev-pub1_publish=http://admin:admin@127.0.0.1:4503", "-U", "dev-pub2_publish=http://admin:admin@127.0.0.1:4504", "-A", "--output-value", "NONE"}, []string{"dev-auth_author"}) } func TestPublishInstanceByURL(t *testing.T) { - testInstanceList(t, []string{"instance", "list", "-U", "dev-auth_author=http://admin:admin@127.0.0.1:4502", "-U", "dev-pub1_publish=http://admin:admin@127.0.0.1:4503", "-U", "dev-pub2_publish=http://admin:admin@127.0.0.1:4504", "-P", "--output-value", "NONE"}, true, []string{"dev-pub1_publish", "dev-pub2_publish"}) + testInstanceList(t, []string{"instance", "list", "-U", "dev-auth_author=http://admin:admin@127.0.0.1:4502", "-U", "dev-pub1_publish=http://admin:admin@127.0.0.1:4503", "-U", "dev-pub2_publish=http://admin:admin@127.0.0.1:4504", "-P", "--output-value", "NONE"}, []string{"dev-pub1_publish", "dev-pub2_publish"}) } From 69ac17e8fe7e1d9971dae019fc64160338fe58b4 Mon Sep 17 00:00:00 2001 From: Dominik Przybyl Date: Fri, 22 Nov 2024 22:10:14 +0100 Subject: [PATCH 9/9] refactor --- cmd/aem/instance_list_int_test.go | 18 +++++++++++----- cmd/aem/root.go | 2 ++ pkg/instance_manager.go | 35 ++++++++++++++----------------- 3 files changed, 31 insertions(+), 24 deletions(-) diff --git a/cmd/aem/instance_list_int_test.go b/cmd/aem/instance_list_int_test.go index f26a98e4..f4277082 100644 --- a/cmd/aem/instance_list_int_test.go +++ b/cmd/aem/instance_list_int_test.go @@ -52,7 +52,7 @@ func TestPublishInstances(t *testing.T) { testInstanceList(t, []string{"instance", "list", "-P", "--output-value", "NONE"}, []string{"local_publish"}) } -func TestInstanceByID(t *testing.T) { +func TestIDInstance(t *testing.T) { testInstanceList(t, []string{"instance", "list", "-I", "local_author", "--output-value", "NONE"}, []string{"local_author"}) } @@ -60,10 +60,6 @@ func TestInstanceByURL(t *testing.T) { testInstanceList(t, []string{"instance", "list", "-U", "http://admin:admin@127.0.0.1:4502", "-U", "http://admin:admin@127.0.0.1:4503", "-U", "test_author=http://admin:admin@127.0.0.1:4502", "--output-value", "NONE"}, []string{"remote_adhoc_1", "remote_adhoc_2", "test_author"}) } -func TestInstanceByIDOrURL(t *testing.T) { - testInstanceList(t, []string{"instance", "list", "-I", "local_publish", "-U", "http://admin:admin@127.0.0.1:4502", "--output-value", "NONE"}, []string{"local_publish", "remote_adhoc"}) -} - func TestAuthorInstanceByURL(t *testing.T) { testInstanceList(t, []string{"instance", "list", "-U", "dev-auth_author=http://admin:admin@127.0.0.1:4502", "-U", "dev-pub1_publish=http://admin:admin@127.0.0.1:4503", "-U", "dev-pub2_publish=http://admin:admin@127.0.0.1:4504", "-A", "--output-value", "NONE"}, []string{"dev-auth_author"}) } @@ -71,3 +67,15 @@ func TestAuthorInstanceByURL(t *testing.T) { func TestPublishInstanceByURL(t *testing.T) { testInstanceList(t, []string{"instance", "list", "-U", "dev-auth_author=http://admin:admin@127.0.0.1:4502", "-U", "dev-pub1_publish=http://admin:admin@127.0.0.1:4503", "-U", "dev-pub2_publish=http://admin:admin@127.0.0.1:4504", "-P", "--output-value", "NONE"}, []string{"dev-pub1_publish", "dev-pub2_publish"}) } + +func TestAuthorIDInstanceByURL(t *testing.T) { + testInstanceList(t, []string{"instance", "list", "-U", "dev-auth_author=http://admin:admin@127.0.0.1:4502", "-U", "dev-pub1_publish=http://admin:admin@127.0.0.1:4503", "-U", "dev-pub2_publish=http://admin:admin@127.0.0.1:4504", "-A", "-I", "dev-auth_author", "--output-value", "NONE"}, []string{"dev-auth_author"}) +} + +func TestPublishIDInstanceByURL(t *testing.T) { + testInstanceList(t, []string{"instance", "list", "-U", "dev-auth_author=http://admin:admin@127.0.0.1:4502", "-U", "dev-pub1_publish=http://admin:admin@127.0.0.1:4503", "-U", "dev-pub2_publish=http://admin:admin@127.0.0.1:4504", "-P", "-I", "dev-pub1_publish", "--output-value", "NONE"}, []string{"dev-pub1_publish"}) +} + +func TestIDInstanceByURL(t *testing.T) { + testInstanceList(t, []string{"instance", "list", "-U", "dev-auth_author=http://admin:admin@127.0.0.1:4502", "-U", "dev-pub1_publish=http://admin:admin@127.0.0.1:4503", "-U", "dev-pub2_publish=http://admin:admin@127.0.0.1:4504", "-I", "dev-auth_author", "-I", "dev-pub1_publish", "--output-value", "NONE"}, []string{"dev-auth_author", "dev-pub1_publish"}) +} diff --git a/cmd/aem/root.go b/cmd/aem/root.go index 107fdcf0..de77da51 100644 --- a/cmd/aem/root.go +++ b/cmd/aem/root.go @@ -80,6 +80,8 @@ func (c *CLI) rootFlags(cmd *cobra.Command) { cmd.PersistentFlags().BoolP("instance-publish", "P", cv.GetBool("instance.filter.publishes"), "Use only AEM publish instance(s)") _ = cv.BindPFlag("instance.filter.publishes", cmd.PersistentFlags().Lookup("instance-publish")) + cmd.MarkFlagsMutuallyExclusive("instance-author", "instance-publish") + cmd.PersistentFlags().String("instance-processing", cv.GetString("instance.processing_mode"), "Controls processing mode for instances ("+(strings.Join(instance.ProcessingModes(), "|")+")")) _ = cv.BindPFlag("instance.processing_mode", cmd.PersistentFlags().Lookup("instance-processing")) } diff --git a/pkg/instance_manager.go b/pkg/instance_manager.go index 515982cf..0a24c0e5 100644 --- a/pkg/instance_manager.go +++ b/pkg/instance_manager.go @@ -84,8 +84,8 @@ func (im *InstanceManager) All() []Instance { } func (im *InstanceManager) newAdHocOrFromConfig() []Instance { - var result []Instance if len(im.AdHocURLs) > 0 { + var result []Instance for adHocIndex, adHocValue := range im.AdHocURLs { iURL, err := im.newAdhoc(adHocValue, adHocIndex, len(im.AdHocURLs)) if err != nil { @@ -93,36 +93,24 @@ func (im *InstanceManager) newAdHocOrFromConfig() []Instance { } result = append(result, *iURL) } + return result } - var instances []Instance cv := im.aem.config.Values() configIDs := maps.Keys(cv.GetStringMap("instance.config")) if len(configIDs) > 0 { + var result []Instance for _, id := range configIDs { cv.SetDefault(fmt.Sprintf("instance.config.%s.active", id), true) active := cv.GetBool(fmt.Sprintf("instance.config.%s.active", id)) if active { if i := im.newFromConfig(id); i != nil { - instances = append(instances, *i) + result = append(result, *i) } } } - } else { - instances = im.NewLocalPair() + return result } - if len(im.FilterIDs) > 0 { - for _, i := range instances { - for _, filterID := range im.FilterIDs { - if i.id == filterID { - result = append(result, i) - break - } - } - } - } else if len(im.AdHocURLs) == 0 { - result = instances - } - return result + return im.NewLocalPair() } func (im *InstanceManager) newAdhoc(value string, current int, total int) (*Instance, error) { @@ -179,7 +167,16 @@ func (im *InstanceManager) filter(instances []Instance) []Instance { result := []Instance{} for _, i := range instances { if im.FilterAuthors == im.FilterPublishes || im.FilterAuthors && i.IsAuthor() || im.FilterPublishes && i.IsPublish() { - result = append(result, i) + if len(im.FilterIDs) == 0 { + result = append(result, i) + } else { + for _, filterID := range im.FilterIDs { + if i.id == filterID { + result = append(result, i) + break + } + } + } } } sort.SliceStable(result, func(i, j int) bool {