Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix code to open Terraform resource documentation #67

Merged
merged 13 commits into from
Mar 18, 2024
Merged
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,13 @@ Type `C-c C-d C-c` to kill the URL (i.e. copy it to the clipboard) for the docum
You can also type `C-c C-d C-r` to insert a comment containing a link to
this documentation right above the resource or data block.

This feature requires either:

- a `required_provider` declaration in any `.tf` file in current directory
(see [Terraform doc](https://developer.hashicorp.com/terraform/language/providers/requirements#requiring-providers))
- a working `terraform providers` command. This command may require a
valid token (at least for AWS).

## Customize Variables

#### `terraform-indent-level`(Default: `2`)
Expand Down
56 changes: 48 additions & 8 deletions terraform-mode.el
Original file line number Diff line number Diff line change
Expand Up @@ -252,18 +252,58 @@
(when (re-search-forward (concat "/\\(.*?\\)/" provider "\\]") nil t)
(match-string 1)))))

(defun terraform--get-resource-provider-source (provider &optional dir)
"Return Terraform provider source for PROVIDER located in DIR.
Terraform provider source is searched in 'required_provider' declaration
in current buffer or in other Terraform files located in the same directory
of the file of current buffer. If still not found, the provider source is
searched by running command 'terraform providers'.
The DIR parameter is optional and used only for tests."
(goto-char (point-min))
;; find current directory if it's not specified in arguments
(if (and (not dir) buffer-file-name) (setq dir (file-name-directory buffer-file-name)))
(let (tf-files
;; try to find provider source in current buffer
(provider-source (terraform--get-resource-provider-source-in-buffer provider)))
;; check if terraform provider-source was found
(when (and (= (length provider-source) 0) dir)
;; find all terraform files of this project. One of them
;; should contain required_provider declaration
(setq tf-files (directory-files dir nil "^[[:alnum:][:blank:]_.-]+\\.tf$")))
;; iterate on terraform files until a provider source is found
(while (and (= (length provider-source) 0) tf-files)
(with-temp-buffer
(let* ((file (pop tf-files))
(file-path (if dir (concat dir "/" file) file)))
(insert-file-contents file-path)
;; look for provider source in a terraform file
(setq provider-source (terraform--get-resource-provider-source-in-buffer provider)))))
provider-source))

(defun terraform--get-resource-provider-source-in-buffer (provider)
"Search and return provider namespace for PROVIDER in current buffer. Return nil if not found."
(goto-char (point-min))
(if (and (re-search-forward "^terraform[[:blank:]]*{" nil t)
(re-search-forward "^[[:blank:]]*required_providers[[:blank:]]*{" nil t)
(re-search-forward (concat "^[[:blank:]]*" provider "[[:blank:]]*=[[:blank:]]*{") nil t)
(re-search-forward "^[[:blank:]]*source[[:blank:]]*=[[:blank:]]*\"\\([a-z/]+\\)\"" nil t))
(match-string 1)))

(defun terraform--resource-url (resource doc-dir)
"Return the url containing the documentation for RESOURCE using DOC-DIR."
(let* ((provider (terraform--extract-provider resource))
(provider-ns (terraform--get-resource-provider-namespace provider))
;; search provider source in terraform files
(provider-source (terraform--get-resource-provider-source provider))
(resource-name (terraform--extract-resource resource)))
(if provider-ns
(format "https://registry.terraform.io/providers/%s/%s/latest/docs/%s/%s"
provider-ns
provider
doc-dir
resource-name)
(user-error "Can not determine the provider namespace for %s" provider))))
(when (= (length provider-source) 0)
;; fallback to old method with terraform providers command
(setq provider-source (concat
(terraform--get-resource-provider-namespace provider)
"/" provider)))
(if (> (length provider-source) 0)
(format "https://registry.terraform.io/providers/%s/latest/docs/%s/%s"
provider-source doc-dir resource-name)
(user-error "Can not determine the provider source for %s" provider))))

(defun terraform--resource-url-at-point ()
(save-excursion
Expand Down
1 change: 1 addition & 0 deletions test/fixtures/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# blah blah
13 changes: 13 additions & 0 deletions test/fixtures/provider.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# blah blah
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5"
}
azurerm = {
source = "hashicorp/azurerm"
version = "~> 3.47"
}
}
}
45 changes: 45 additions & 0 deletions test/test-command.el
Original file line number Diff line number Diff line change
Expand Up @@ -156,4 +156,49 @@ resource \"elasticstack_elasticsearch_security_user\" \"filebeat_writer\" {
(cl-letf (((symbol-function 'terraform--get-resource-provider-namespace) (lambda (prov) "elastic")))
(should (equal (terraform--resource-url-at-point) "https://registry.terraform.io/providers/elastic/elasticstack/latest/docs/resources/elasticsearch_security_user")))))

(ert-deftest command--terraform--get-resource-provider-source-in-buffer ()
(with-terraform-temp-buffer
"
# blah blah
terraform {
required_providers {
aws = {
source = \"hashicorp/aws\"
version = \"~> 5\"
}
azurerm = {
source = \"hashicorp/azurerm\"
version = \"~> 3.47\"
}
}
}
"
(should (equal (terraform--get-resource-provider-source-in-buffer "azurerm") "hashicorp/azurerm"))
;;(should (equal (terraform--get-resource-provider-source-in-buffer "plop") nil))
(should (equal (terraform--get-resource-provider-source-in-buffer "aws") "hashicorp/aws"))))

;; required_providers is defined in current buffer
(ert-deftest command--terraform--get-resource-provider-source ()
(with-terraform-temp-buffer
"
# blah blah
terraform {
required_providers {
aws = {
source = \"hashicorp/aws\"
version = \"~> 5\"
}
azurerm = {
source = \"hashicorp/azurerm\"
version = \"~> 3.47\"
}
}
}
Comment on lines +184 to +196
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here we should probably put some different content so it really loads the data from the file and not from the buffer (as a fallback).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Loading provider source from this file is done in test command--terraform--get-resource-provider-source-provider-in-file line 201.

"
(should (equal (terraform--get-resource-provider-source "aws" "test/fixtures") "hashicorp/aws"))))

;; required_providers is defined in another file
(ert-deftest command--terraform--get-resource-provider-source-provider-in-file ()
(should (equal (terraform--get-resource-provider-source "aws" "test/fixtures") "hashicorp/aws")))

;;; test-command.el ends here
Loading