diff --git a/estate/assets/js/api/messages.js b/estate/assets/js/api/messages.js
index 8587867..046b19c 100644
--- a/estate/assets/js/api/messages.js
+++ b/estate/assets/js/api/messages.js
@@ -33,7 +33,6 @@ export function error(message) {
export function handleResponseError(err) {
if (err.response || err.errors) {
var data = err.response.data
- console.log(data)
var message = ""
if (isArray(data.errors)) {
each(data.errors, (item) => {
diff --git a/estate/assets/js/api/terraform.js b/estate/assets/js/api/terraform.js
index 1f8c2f1..255bd2a 100644
--- a/estate/assets/js/api/terraform.js
+++ b/estate/assets/js/api/terraform.js
@@ -118,6 +118,17 @@ export function updateTemplateOfTemplateInstance(id) {
return req
}
+export function diffTemplateInstance(id) {
+ const req = axios.get(`/api/v1/terraform/templateinstance/${id}/diff_latest/`)
+ req.then((res) => {
+ dispatch({
+ type: "LOAD_TEMPLATE_INSTANCE_DIFF",
+ payload: res.data
+ })
+ }, messages.handleResponseError)
+ return req
+}
+
export function removeTemplateFromNamespace(slug, id) {
const req = axios.delete(`/api/v1/terraform/templateinstance/${id}/`)
req.then(() => {
diff --git a/estate/assets/js/components/DiffModal.jsx b/estate/assets/js/components/DiffModal.jsx
new file mode 100644
index 0000000..627563d
--- /dev/null
+++ b/estate/assets/js/components/DiffModal.jsx
@@ -0,0 +1,27 @@
+import React from "react"
+import Diff from "react-diff"
+import Modal from "./Modal"
+
+
+export default class ConfirmModal extends React.Component {
+ getResult() {
+ return {}
+ }
+ render() {
+ return(
+
+
+
+ )
+ }
+}
diff --git a/estate/assets/js/components/Modal.jsx b/estate/assets/js/components/Modal.jsx
index dee585a..078dd3b 100644
--- a/estate/assets/js/components/Modal.jsx
+++ b/estate/assets/js/components/Modal.jsx
@@ -34,6 +34,8 @@ class Modal extends React.Component {
this.closeModal()
}
openModal() {
+ if (this.props.performLoad)
+ this.props.performLoad()
if (!this.props.disabled)
this.setState({modalIsOpen: true})
}
diff --git a/estate/assets/js/components/TerraformNamespaceItem.jsx b/estate/assets/js/components/TerraformNamespaceItem.jsx
index a9a30f1..5c38174 100644
--- a/estate/assets/js/components/TerraformNamespaceItem.jsx
+++ b/estate/assets/js/components/TerraformNamespaceItem.jsx
@@ -7,6 +7,7 @@ import ReactTooltip from "react-tooltip"
import Ansi from "ansi-to-react"
import Editor from "./Editor"
import ConfirmModal from "./ConfirmModal"
+import DiffModal from "./DiffModal"
import TerraformNamespaceAddFileModal from "./TerraformNamespaceAddFileModal"
import TerraformTemplateRenderer from "./TerraformTemplateRenderer"
import * as terraform from "../api/terraform"
@@ -297,12 +298,21 @@ class TerraformNamespaceItem extends React.Component {
})
return elements
}
- createTemplateUpdateButton(id) {
+ createTemplateUpdateButton(locked, templateInstance) {
+ if (locked)
+ return null
+ if (!templateInstance.is_outdated)
+ return null
+ var id = templateInstance.pk
return (
-
)
}
@@ -326,7 +336,6 @@ class TerraformNamespaceItem extends React.Component {
@@ -635,6 +644,9 @@ const mapStateToProps = (state, ownProps) => {
updateFile: terraform.updateFile,
updateTemplateInstance: terraform.updateTemplateInstance,
updateTemplateOfTemplateInstance: terraform.updateTemplateOfTemplateInstance,
+ diffTemplateInstance: terraform.diffTemplateInstance,
+ diffTemplateOld: state.terraform.diffTemplateOld,
+ diffTemplateNew: state.terraform.diffTemplateNew,
planOutput: state.terraform.planOutput,
applyOutput: state.terraform.applyOutput,
stateObject: state.terraform.stateObject,
diff --git a/estate/assets/js/components/TerraformTemplateItem.jsx b/estate/assets/js/components/TerraformTemplateItem.jsx
index 64d3066..9c78146 100644
--- a/estate/assets/js/components/TerraformTemplateItem.jsx
+++ b/estate/assets/js/components/TerraformTemplateItem.jsx
@@ -186,20 +186,20 @@ class TerraformTemplateItem extends React.Component {
Edit {template.version}
diff --git a/estate/assets/js/reducers/terraform.js b/estate/assets/js/reducers/terraform.js
index 13a464d..445dea4 100644
--- a/estate/assets/js/reducers/terraform.js
+++ b/estate/assets/js/reducers/terraform.js
@@ -17,6 +17,8 @@ var initialState = {
files: [],
templates: [],
renderedTemplate: "{}",
+ diffTemplateOld: "{}",
+ diffTemplateNew: "{}",
templatesPage: 0,
templatesPages: 0,
}
@@ -145,4 +147,10 @@ export default createReducer(initialState, {
state = set(["renderedTemplate"])(action.payload)(state)
return state
},
+
+ ["LOAD_TEMPLATE_INSTANCE_DIFF"]: (state, action) => {
+ state = set(["diffTemplateOld"])(action.payload.old)(state)
+ state = set(["diffTemplateNew"])(action.payload.new)(state)
+ return state
+ }
})
diff --git a/estate/settings/drf.py b/estate/settings/drf.py
index e4a0aec..d831b9b 100644
--- a/estate/settings/drf.py
+++ b/estate/settings/drf.py
@@ -32,6 +32,7 @@ def api_exception_handler(exc, context):
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.BasicAuthentication',
'rest_framework.authentication.TokenAuthentication',
+ 'rest_framework.authentication.SessionAuthentication',
),
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticated',
@@ -61,7 +62,7 @@ def api_exception_handler(exc, context):
},
'LOGIN_URL': 'rest_framework:login',
'LOGOUT_URL': 'rest_framework:logout',
- 'USE_SESSION_AUTH': False,
+ 'USE_SESSION_AUTH': True,
'APIS_SORTER': 'alpha',
'JSON_EDITOR': True,
'VALIDATOR_URL': None
diff --git a/estate/terraform/views/template.py b/estate/terraform/views/template.py
index 216547f..1b3ee5e 100644
--- a/estate/terraform/views/template.py
+++ b/estate/terraform/views/template.py
@@ -6,6 +6,7 @@
from estate.core.views import HistoricalSerializer, HistoryMixin, OwnsNamespace
from estate.core import renderer
+
Namespace = apps.get_model("terraform.Namespace")
Template = apps.get_model("terraform.Template")
TemplateInstance = apps.get_model("terraform.TemplateInstance")
@@ -127,6 +128,11 @@ def update(self, instance, validated_data):
return super(TemplateInstanceSerializer, self).update(instance, validated_data)
+class TemplateDiffSerializer(serializers.Serializer):
+ old = serializers.JSONField(read_only=True)
+ new = serializers.JSONField(read_only=True)
+
+
class TemplateInstanceFilter(filters.FilterSet):
class Meta:
@@ -136,13 +142,47 @@ class Meta:
class TemplateInstanceApiView(HistoryMixin, viewsets.ModelViewSet):
queryset = TemplateInstance.objects.all()
- serializer_class = TemplateInstanceSerializer
+ serializers = {
+ "default": TemplateInstanceSerializer,
+ "diff_latest": TemplateDiffSerializer,
+ }
filter_class = TemplateInstanceFilter
permission_classes = (OwnsNamespace, )
filter_fields = ("slug",)
search_fields = ("title",)
ordering_fields = ("title", "created", "modified")
+ def get_serializer_class(self):
+ return self.serializers.get(self.action, self.serializers["default"])
+
+ @decorators.detail_route(methods=["GET"])
+ def diff_latest(self, request, *args, **kwargs):
+ instance = self.get_object()
+ latest_template = Template.all_objects.get(pk=instance.template.id)
+ old_data = {
+ "template_str": instance.template.body,
+ "inputs": instance.inputs,
+ "overrides": instance.overrides,
+ "disable": instance.disable,
+ }
+ new_data = {
+ "template_str": latest_template.body,
+ "inputs": instance.inputs,
+ "overrides": instance.overrides,
+ "disable": instance.disable,
+ }
+ try:
+ data = {
+ "old": renderer.render_template(**old_data),
+ "new": renderer.render_template(**new_data),
+ }
+ except Exception as e:
+ raise exceptions.APIException(str(e))
+ serializer = self.get_serializer(data=data)
+ serializer.is_valid(raise_exception=True)
+ headers = self.get_success_headers(serializer.data)
+ return response.Response(data, status=status.HTTP_200_OK, headers=headers)
+
@decorators.detail_route(methods=["POST"])
def update_template(self, request, *args, **kwargs):
instance = self.get_object()
diff --git a/estate/urls.py b/estate/urls.py
index af6e78a..cf47903 100644
--- a/estate/urls.py
+++ b/estate/urls.py
@@ -23,7 +23,6 @@
url(r'^api/$', RedirectView.as_view(url='/api/swagger/')),
url(r'^api/schema/$', base_schema_view),
url(r'^api/swagger/', swagger_view),
- url(r'^api/docs/', include_docs_urls(title=title), name="api-docs"),
url(r'^api/v1/terraform/', include('estate.terraform.urls')),
# Acts as a catchall for everything else and react router will take over
url(r'^', TemplateView.as_view(template_name='index.html')),