feat(go): add Go workflow scripts for ONTAP automation#55
Conversation
A CI check failed — here is how to fix itWorkflow: PR Guard · Run #84 · Failed jobs: 1 commitlintCommit message does not follow Conventional Commits. Required format: Valid types: To rewrite the last commit: git commit --amend -m "feat(python): your description"
git push --force-with-leasePush a fix and CI re-runs automatically; this comment updates with the next failure (or stays put if the same check fails again). Stuck? Comment on the PR and a maintainer will help — typical response time is 1 business day. Auto-generated · explain-failure.yml |
There was a problem hiding this comment.
Pull request overview
This PR adds Go implementations of existing ONTAP automation workflows, introducing a small stdlib-based ONTAP REST client plus Go scripts that mirror the SnapMirror and cluster-setup examples already present in Python/Ansible/Terraform.
Changes:
- Added a new Go module (
go 1.22) and a lightweightontapclientwrapper for ONTAP REST calls. - Implemented Go workflow scripts for cluster setup and SnapMirror provisioning/test-failover/cleanup.
- Updated
.gitignoreto exclude Go build/test outputs undergo/.
Reviewed changes
Copilot reviewed 7 out of 8 changed files in this pull request and generated 10 comments.
Show a summary per file
| File | Description |
|---|---|
go/go.mod |
Defines the Go module and language version baseline. |
go/ontapclient/ontap_client.go |
Adds a minimal REST client (auth, JSON handling, job polling, relationship polling). |
go/cluster_setup_basic/main.go |
Go implementation of basic 2-node cluster bootstrap workflow. |
go/snapmirror_provision_src_managed/main.go |
Go implementation of source-managed SnapMirror provisioning flow. |
go/snapmirror_provision_dest_managed/main.go |
Go implementation of destination-managed provisioning, including auto peer setup. |
go/snapmirror_test_failover/main.go |
Go implementation to create/tag a test-failover FlexClone and resync SnapMirror. |
go/snapmirror_cleanup_test_failover/main.go |
Go implementation to locate/delete the tagged test-failover clone safely. |
.gitignore |
Ignores go/**/*.exe and go/**/*.test outputs. |
| func (c *Client) PollJob(jobUUID string, intervalSecs int) (map[string]interface{}, error) { | ||
| if intervalSecs <= 0 { | ||
| intervalSecs = 10 | ||
| } | ||
| for { | ||
| result, err := c.Get(fmt.Sprintf("/cluster/jobs/%s", jobUUID), | ||
| map[string]string{"fields": "state,message,error,code"}) | ||
| if err != nil { | ||
| return nil, fmt.Errorf("poll job %s: %w", jobUUID, err) | ||
| } | ||
| state, _ := result["state"].(string) | ||
| log.Printf(" job %s — state=%s", jobUUID, state) | ||
| if state != "running" { | ||
| return result, nil | ||
| } | ||
| time.Sleep(time.Duration(intervalSecs) * time.Second) | ||
| } | ||
| } |
| cloneVol := map[string]interface{}{} | ||
| if vols := ontapclient.Records(cloneVolResp); len(vols) > 0 { | ||
| cloneVol = vols[0] | ||
| } | ||
| cloneUUID := ontapclient.NestedStr(cloneVol, "uuid") | ||
| log.Printf("CLONE | name=%s | uuid=%s | state=%s | junction=%s", | ||
| ontapclient.NestedStr(cloneVol, "name"), cloneUUID, | ||
| ontapclient.NestedStr(cloneVol, "state"), | ||
| ontapclient.NestedStr(cloneVol, "nas", "path")) | ||
| return cloneName, cloneUUID |
| peerName := "" | ||
| if peers := ontapclient.Records(peerResp); len(peers) > 0 { | ||
| peerName = ontapclient.NestedStr(peers[0], "name") | ||
| } | ||
| log.Printf("CLUSTER PEER | name=%s", peerName) | ||
|
|
| func fetchCreatedPeerNames(src, dst *ontapclient.Client) (string, string, string) { | ||
| okStates := map[string]bool{"available": true, "partial": true, "pending": true} | ||
| dstCP, _ := dst.Get(pathClusterPeers, map[string]string{"fields": peerFields, "max_records": "10"}) | ||
| dstPeer := map[string]interface{}{} | ||
| for _, p := range ontapclient.Records(dstCP) { | ||
| if okStates[ontapclient.NestedStr(p, "status", "state")] { | ||
| dstPeer = p | ||
| break | ||
| } | ||
| } | ||
| srcCP, _ := src.Get(pathClusterPeers, map[string]string{"fields": peerFields, "max_records": "10"}) | ||
| srcPeer := map[string]interface{}{} | ||
| for _, p := range ontapclient.Records(srcCP) { | ||
| if okStates[ontapclient.NestedStr(p, "status", "state")] { | ||
| srcPeer = p | ||
| break | ||
| } | ||
| } | ||
| log.Printf("CLUSTER PEER | dst sees src as '%s'", ontapclient.NestedStr(dstPeer, "name")) | ||
| return ontapclient.NestedStr(srcPeer, "name"), | ||
| ontapclient.NestedStr(dstPeer, "name"), | ||
| ontapclient.NestedStr(dstPeer, "uuid") | ||
| } |
| // AUTO mode (SOURCE_VOLUME=* or unset): | ||
| // | ||
| // Queries both clusters, picks the one with the most recently created DP volume. | ||
| // |
Add Go implementations of the existing Python, Ansible, and Terraform workflows.
After POST /cluster ONTAP restarts its management stack, forcibly closing the TCP connection. The previous code treated any poll error as fatal; this change retries on network-level errors (expected reboot drop) and only fails on HTTP-level errors (4xx/5xx). Also adds 'errors' import required by errors.As().
Summary
Go implementations of the existing ONTAP workflows. Same use cases as Python/Ansible/Terraform — picked Go because some teams already have it in their stack and stdlib-only means nothing extra to install.
Changes
go.mod — module definition, Go 1.22+
ontapclient — thin HTTP wrapper around ONTAP REST (auth, job polling, error handling)
cluster_setup_basic — bootstrap a 2-node cluster
snapmirror_provision_src_managed — SnapMirror setup from source side
snapmirror_provision_dest_managed — SnapMirror setup from dest side, handles peer setup automatically
snapmirror_test_failover — FlexClone the dest volume for a test failover
snapmirror_cleanup_test_failover — tear down the test clone
.gitignore — added *.exe and *.test
Checklist
No hardcoded IPs or passwords — everything comes from env vars, defaults are empty strings
go build Downloads. and go vet Downloads. clean
catalog.yaml — skipped, Go isn't a catalogued tool yet
Test Report
Environment: ONTAP 9.8 vsim, Go 1.22.3, Windows 11
Ran all five scripts against a two-node vsim pair. Re-running each script a second time skips already-existing resources without errors.
Related issues
Part of the Go language support track.