Repository: saltstack/salt
Pull request: Adding the ability to clear and show the pillar cache enabled when pillar_cache is True.
### What does this PR do?
Adding the ability to clear and show the pillar cache enabled when pillar_cache is True.

### What issues does this PR fix or reference?
Fixes:  #37080 

### Merge requirements satisfied?
**[NOTICE] Bug fixes or features added to Salt require tests.**
<!-- Please review the [test documentation](https://docs.saltstack.com/en/master/topics/tutorials/writing_tests.html) for details on how to implement tests into Salt's test suite. -->
- [ ] Docs
- [x] Changelog - https://docs.saltstack.com/en/master/topics/development/changelog.html
- [x] Tests written/updated

### Commits signed with GPG?
Yes

Please review [Salt's Contributing Guide](https://docs.saltstack.com/en/master/topics/development/contributing.html) for best practices.

See GitHub's [page on GPG signing](https://help.github.com/articles/signing-commits-using-gpg/) for more information about signing commits with GPG.


Problem statement:
Clearing pillar cache from Master leaves stray files & does not appear to clear it
## Description of Issue/Question

Using the pillar cache with a disk backend. The `cache.clear_pillar` runner appears to clear the cache upon immediately calling `cache.pillar`, but the files still persist on the disk, and if it's within the cache TTL, the cached pillar data still gets retrieved, since those files still persist.

This is notable because we leverage a _custom external pillar_ that takes about 30 seconds to retrieve its data, so it's very obvious whether or not the cached data is being retrieved. I emphasized the fact that it is a custom external pillar in the event that this issue is a complication of using one.
### Setup

(Please provide relevant configs and/or SLS files (Be sure to remove sensitive info).)
Master config:

```
pillar_cache: True
pillar_cache_backend: disk
pillar_cache_ttl: 86400
```
### Steps to Reproduce Issue

(Include debug logs if possible and relevant.)
1) Populate the cache: `salt-call pillar.items`.
2) Attempts to clear cache with the runner:

``` bash
(salt) root@:pillar_cache # salt-run cache.clear_pillar '*'
True
(salt) root@:pillar_cache # salt-run cache.pillar
i-ot7rj6eg:
    ----------
i-v8vpgxba:
    ----------
(salt) root@:pillar_cache # ls -l
total 40
-rw-r--r-- 1 root root 17956 Oct 18 18:31 i-ot7rj6eg
-rw-r--r-- 1 root root 17956 Oct 18 18:54 i-v8vpgxba
```

3) Run `salt-call pillar.items` again.

```
[ERROR   ] Unable to read instance data, giving up
[INFO    ] Not an EC2 instance, skipping
[DEBUG   ] Connecting to master. Attempt 1 of 1
[DEBUG   ] Initializing new AsyncAuth for ('/etc/salt/pki/minion', 'i-v8vpgxba', 'tcp://172.254.254.100:4506')
[DEBUG   ] Generated random reconnect delay between '1000ms' and '11000ms' (9163)
[DEBUG   ] Setting zmq_reconnect_ivl to '9163ms'
[DEBUG   ] Setting zmq_reconnect_ivl_max to '11000ms'
[DEBUG   ] Initializing new AsyncZeroMQReqChannel for ('/etc/salt/pki/minion', 'i-v8vpgxba', 'tcp://172.254.254.100:4506', 'clear')
[DEBUG   ] Decrypting the current master AES key
[DEBUG   ] Loaded minion key: /etc/salt/pki/minion/minion.pem
[DEBUG   ] SaltEvent PUB socket URI: /var/run/salt/minion/minion_event_4b91cc8893_pub.ipc
[DEBUG   ] SaltEvent PULL socket URI: /var/run/salt/minion/minion_event_4b91cc8893_pull.ipc
[DEBUG   ] Initializing new IPCClient for path: /var/run/salt/minion/minion_event_4b91cc8893_pull.ipc
[DEBUG   ] Sending event: tag = salt/auth/creds; data = {'_stamp': '2016-10-18T18:58:14.714370', 'creds': {'publish_port': 4505, 'aes': 'bWRJlUX08onQaBa1U4b9AV5JmEiS/QXwgZwWyNDrB1umL7ww38V2jr4NqNvDo1EUXQePf
PsAtDo=', 'master_uri': 'tcp://172.254.254.100:4506'}, 'key': ('/etc/salt/pki/minion', 'i-v8vpgxba', 'tcp://172.254.254.100:4506')}
[DEBUG   ] Loaded minion key: /etc/salt/pki/minion/minion.pem
[DEBUG   ] Determining pillar cache
[DEBUG   ] Initializing new AsyncZeroMQReqChannel for ('/etc/salt/pki/minion', 'i-v8vpgxba', 'tcp://172.254.254.100:4506', 'aes')
[DEBUG   ] Initializing new AsyncAuth for ('/etc/salt/pki/minion', 'i-v8vpgxba', 'tcp://172.254.254.100:4506')
[DEBUG   ] Loaded minion key: /etc/salt/pki/minion/minion.pem
[DEBUG   ] LazyLoaded jinja.render
[DEBUG   ] LazyLoaded yaml.render
[DEBUG   ] LazyLoaded pillar.items
[DEBUG   ] Determining pillar cache
[DEBUG   ] Initializing new AsyncZeroMQReqChannel for ('/etc/salt/pki/minion', 'i-v8vpgxba', 'tcp://172.254.254.100:4506', 'aes')
[DEBUG   ] Initializing new AsyncAuth for ('/etc/salt/pki/minion', 'i-v8vpgxba', 'tcp://172.254.254.100:4506')
[DEBUG   ] Loaded minion key: /etc/salt/pki/minion/minion.pem
[DEBUG   ] Initializing new AsyncZeroMQReqChannel for ('/etc/salt/pki/minion', 'i-v8vpgxba', 'tcp://172.254.254.100:4506', 'aes')
[DEBUG   ] Initializing new AsyncAuth for ('/etc/salt/pki/minion', 'i-v8vpgxba', 'tcp://172.254.254.100:4506')
[DEBUG   ] LazyLoaded nested.output
```

4) Repeat step 2, then delete the files in `pillar_cache`.
5) The pillar refreshes its data from the external pillar as expected. The `SaltReqTimeoutErrors` are safe to ignore.

```
[ERROR   ] Unable to read instance data, giving up
[INFO    ] Not an EC2 instance, skipping
[DEBUG   ] Connecting to master. Attempt 1 of 1
[DEBUG   ] Initializing new AsyncAuth for ('/etc/salt/pki/minion', 'i-v8vpgxba', 'tcp://172.254.254.100:4506')
[DEBUG   ] Generated random reconnect delay between '1000ms' and '11000ms' (10703)
[DEBUG   ] Setting zmq_reconnect_ivl to '10703ms'
[DEBUG   ] Setting zmq_reconnect_ivl_max to '11000ms'
[DEBUG   ] Initializing new AsyncZeroMQReqChannel for ('/etc/salt/pki/minion', 'i-v8vpgxba', 'tcp://172.254.254.100:4506', 'clear')
[DEBUG   ] SaltReqTimeoutError, retrying. (1/7)
[DEBUG   ] SaltReqTimeoutError, retrying. (2/7)
[DEBUG   ] SaltReqTimeoutError, retrying. (3/7)
[DEBUG   ] SaltReqTimeoutError, retrying. (4/7)
[DEBUG   ] SaltReqTimeoutError, retrying. (5/7)
[DEBUG   ] SaltReqTimeoutError, retrying. (6/7)
[DEBUG   ] SaltReqTimeoutError, retrying. (7/7)
```
### Versions Report

(Provided by running `salt --versions-report`. Please also mention any differences in master/minion versions.)

Masters and minions use the same version.

```
Salt Version:
           Salt: 2016.3.3-186-g86ac8bd

Dependency Versions:
           cffi: Not Installed
       cherrypy: Not Installed
       dateutil: 2.5.3
          gitdb: 0.6.4
      gitpython: 2.0.2
          ioflo: Not Installed
         Jinja2: 2.8
        libgit2: Not Installed
        libnacl: Not Installed
       M2Crypto: Not Installed
           Mako: Not Installed
   msgpack-pure: Not Installed
 msgpack-python: 0.4.8
   mysql-python: Not Installed
      pycparser: Not Installed
       pycrypto: 2.6.1
         pygit2: Not Installed
         Python: 2.7.9 (default, Jun 29 2016, 13:08:31)
   python-gnupg: Not Installed
         PyYAML: 3.12
          PyZMQ: 16.0.0
           RAET: Not Installed
          smmap: 0.9.0
        timelib: Not Installed
        Tornado: 4.4.2
            ZMQ: 4.1.5

System Versions:
           dist: debian 8.6 
        machine: x86_64
        release: 3.16.0-4-amd64
         system: Linux
        version: debian 8.6
```

Hints from issue discussion:
I am able to replicate this on 2016.3.3 with just regular pillar files.

```
[root@salt ~]# cat /srv/pillar/test.sls
this: one
that: two
those:
  - three
  - four
  - five
[root@salt ~]# salt-call pillar.items
[WARNING ] /usr/lib/python2.7/site-packages/salt/grains/core.py:1493: DeprecationWarning: The "osmajorrelease" will be a type of an integer.

local:
    ----------
    that:
        two
    this:
        one
    those:
        - three
        - four
        - five
[root@salt ~]# sed -iv 's/five/six/' /srv/pillar/test.sls
[root@salt ~]# cat !$
cat /srv/pillar/test.sls
this: one
that: two
those:
  - three
  - four
  - six
[root@salt ~]# salt-call pillar.items
[WARNING ] /usr/lib/python2.7/site-packages/salt/grains/core.py:1493: DeprecationWarning: The "osmajorrelease" will be a type of an integer.

local:
    ----------
    that:
        two
    this:
        one
    those:
        - three
        - four
        - five
[root@salt ~]# salt-run cache.clear_pillar '*'
[WARNING ] /usr/lib/python2.7/site-packages/salt/grains/core.py:1493: DeprecationWarning: The "osmajorrelease" will be a type of an integer.

True
[root@salt ~]# salt-call pillar.items
[WARNING ] /usr/lib/python2.7/site-packages/salt/grains/core.py:1493: DeprecationWarning: The "osmajorrelease" will be a type of an integer.

local:
    ----------
    that:
        two
    this:
        one
    those:
        - three
        - four
        - five
```

It appears that the cache.clear_pillar is not actually invalidating the cache for some reason.

any news? i have old data in pillar in 2016.11
any news in this one?
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

If this issue is closed prematurely, please leave a comment and we will gladly reopen the issue.

I have the same problem with `2017.7.8` The only option I found is to `rm -f /var/cache/salt/master/pillar_cache/*` but I know this is not clean to do that.
Thank you for updating this issue. It is no longer marked as stale.

ping?
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

If this issue is closed prematurely, please leave a comment and we will gladly reopen the issue.

Thank you for updating this issue. It is no longer marked as stale.

I'm seeing this same behavior with version 3000.3

Commit message: Docs cleanup.  Removing unnecessary saltenv.  Moving test to pytest.

Patch to review:
diff --git a/changelog/37080.added b/changelog/37080.added
new file mode 100644
index 000000000000..ae81d22ef23b
--- /dev/null
+++ b/changelog/37080.added
@@ -0,0 +1 @@
+Adding the ability to clear and show the pillar cache enabled when pillar_cache is True.
diff --git a/salt/pillar/__init__.py b/salt/pillar/__init__.py
index 46204c83f8c9..d19a2e80beaf 100644
--- a/salt/pillar/__init__.py
+++ b/salt/pillar/__init__.py
@@ -431,6 +431,14 @@ def fetch_pillar(self):
         )
         return fresh_pillar.compile_pillar()
 
+    def clear_pillar(self):
+        """
+        Clear the cache
+        """
+        self.cache.clear()
+
+        return True
+
     def compile_pillar(self, *args, **kwargs):  # Will likely just be pillar_dirs
         log.debug(
             "Scanning pillar cache for information about minion %s and pillarenv %s",
diff --git a/salt/runners/cache.py b/salt/runners/cache.py
index 119d9695f6a0..6bb8a6f455cc 100644
--- a/salt/runners/cache.py
+++ b/salt/runners/cache.py
@@ -1,17 +1,12 @@
-# -*- coding: utf-8 -*-
 """
 Return cached data from minions
 """
-from __future__ import absolute_import, print_function, unicode_literals
 
-# Import python libs
 import fnmatch
 import logging
 import os
 
 import salt.cache
-
-# Import salt libs
 import salt.config
 import salt.fileserver.gitfs
 import salt.log
@@ -22,7 +17,6 @@
 import salt.utils.gitfs
 import salt.utils.master
 from salt.exceptions import SaltInvocationError
-from salt.ext import six
 from salt.fileserver import clear_lock as _clear_lock
 
 log = logging.getLogger(__name__)
@@ -133,6 +127,7 @@ def _clear_cache(
     """
     if tgt is None:
         return False
+
     pillar_util = salt.utils.master.MasterPillarUtil(
         tgt,
         tgt_type,
@@ -333,7 +328,7 @@ def clear_git_lock(role, remote=None, **kwargs):
             )
             git_objects.append(obj)
     else:
-        raise SaltInvocationError("Invalid role '{0}'".format(role))
+        raise SaltInvocationError("Invalid role '{}'".format(role))
 
     ret = {}
     for obj in git_objects:
@@ -369,7 +364,7 @@ def cloud(tgt, provider=None):
         salt-run cache.cloud 'salt*'
         salt-run cache.cloud glance.example.org provider=openstack
     """
-    if not isinstance(tgt, six.string_types):
+    if not isinstance(tgt, str):
         return {}
 
     opts = salt.config.cloud_config(
@@ -383,9 +378,9 @@ def cloud(tgt, provider=None):
         return {}
 
     ret = {}
-    for driver, providers in six.iteritems(cloud_cache):
-        for provider, servers in six.iteritems(providers):
-            for name, data in six.iteritems(servers):
+    for driver, providers in cloud_cache.items():
+        for provider, servers in providers.items():
+            for name, data in servers.items():
                 if fnmatch.fnmatch(name, tgt):
                     ret[name] = data
                     ret[name]["provider"] = provider
diff --git a/salt/runners/pillar.py b/salt/runners/pillar.py
index acbcc8d97a88..8a498bacdf27 100644
--- a/salt/runners/pillar.py
+++ b/salt/runners/pillar.py
@@ -1,13 +1,14 @@
-# -*- coding: utf-8 -*-
 """
 Functions to interact with the pillar compiler on the master
 """
-from __future__ import absolute_import, print_function, unicode_literals
 
-# Import salt libs
+import logging
+
 import salt.pillar
 import salt.utils.minions
 
+log = logging.getLogger(__name__)
+
 
 def show_top(minion=None, saltenv="base"):
     """
@@ -97,3 +98,103 @@ def show_pillar(minion="*", **kwargs):
 
     compiled_pillar = pillar.compile_pillar()
     return compiled_pillar
+
+
+def clear_pillar_cache(minion="*", **kwargs):
+    """
+    Clears the cached values when using pillar_cache
+
+    CLI Example:
+
+    Clears the pillar cache for a specific minion:
+
+    .. code-block:: bash
+
+        salt-run pillar.clear_pillar_cache 'minion'
+
+    """
+
+    if not __opts__.get("pillar_cache"):
+        log.info("The pillar_cache is set to False or not enabled.")
+        return False
+
+    ckminions = salt.utils.minions.CkMinions(__opts__)
+    ret = ckminions.check_minions(minion)
+
+    pillarenv = kwargs.pop("pillarenv", None)
+    saltenv = kwargs.pop("saltenv", None)
+
+    pillar_cache = {}
+    for tgt in ret.get("minions", []):
+        saltenv = "base"
+        id_, grains, _ = salt.utils.minions.get_minion_data(tgt, __opts__)
+
+        for key in kwargs:
+            grains[key] = kwargs[key]
+
+        if grains is None:
+            grains = {"fqdn": minion}
+
+        pillar = salt.pillar.PillarCache(
+            __opts__, grains, id_, saltenv, pillarenv=pillarenv
+        )
+        pillar.clear_pillar()
+
+        if __opts__.get("pillar_cache_backend") == "memory":
+            _pillar_cache = pillar.cache
+        else:
+            _pillar_cache = pillar.cache._dict
+
+        if tgt in _pillar_cache and _pillar_cache[tgt]:
+            pillar_cache[tgt] = _pillar_cache.get(tgt).get(pillarenv)
+
+    return pillar_cache
+
+
+def show_pillar_cache(minion="*", **kwargs):
+    """
+    Shows the cached values in pillar_cache
+
+    CLI Example:
+
+    Shows the pillar cache for a specific minion:
+
+    .. code-block:: bash
+
+        salt-run pillar.show_pillar_cache 'minion'
+
+    """
+
+    if not __opts__.get("pillar_cache"):
+        log.info("The pillar_cache is set to False or not enabled.")
+        return False
+
+    ckminions = salt.utils.minions.CkMinions(__opts__)
+    ret = ckminions.check_minions(minion)
+
+    pillarenv = kwargs.pop("pillarenv", None)
+    saltenv = kwargs.pop("saltenv", "base")
+
+    pillar_cache = {}
+    for tgt in ret.get("minions", []):
+        id_, grains, _ = salt.utils.minions.get_minion_data(tgt, __opts__)
+
+        for key in kwargs:
+            grains[key] = kwargs[key]
+
+        if grains is None:
+            grains = {"fqdn": minion}
+
+        pillar = salt.pillar.PillarCache(
+            __opts__, grains, id_, saltenv, pillarenv=pillarenv
+        )
+
+        if __opts__.get("pillar_cache_backend") == "memory":
+            _pillar_cache = pillar.cache
+        else:
+            _pillar_cache = pillar.cache._dict
+
+        if tgt in _pillar_cache and _pillar_cache[tgt]:
+            pillar_cache[tgt] = _pillar_cache.get(tgt).get(pillarenv)
+
+    return pillar_cache
diff --git a/salt/utils/cache.py b/salt/utils/cache.py
index 1f3038e2e3a4..afa0726f6c7e 100644
--- a/salt/utils/cache.py
+++ b/salt/utils/cache.py
@@ -127,6 +127,15 @@ def __delitem__(self, key):
         # Do the same as the parent but also persist
         self._write()
 
+    def clear(self):
+        """
+        Clear the cache
+        """
+        self._key_cache_time.clear()
+        self._dict.clear()
+        # Do the same as the parent but also persist
+        self._write()
+
     def _read(self):
         """
         Read in from disk
diff --git a/tests/pytests/unit/runners/test_pillar.py b/tests/pytests/unit/runners/test_pillar.py
new file mode 100644
index 000000000000..ab042b151a54
--- /dev/null
+++ b/tests/pytests/unit/runners/test_pillar.py
@@ -0,0 +1,126 @@
+"""
+unit tests for the pillar runner
+"""
+
+import logging
+import shutil
+
+import pytest
+import salt.runners.pillar as pillar_runner
+import salt.utils.files
+import salt.utils.gitfs
+import salt.utils.msgpack
+from tests.support.mock import MagicMock, mock_open, patch
+
+log = logging.getLogger(__name__)
+
+MINION_ID = "test-host"
+_CHECK_MINIONS_RETURN = {"minions": [MINION_ID], "missing": []}
+
+
+@pytest.fixture
+def configure_loader_modules():
+    return {
+        pillar_runner: {
+            "__opts__": {
+                "pillar_cache": True,
+                "pillar_cache_backend": "disk",
+                "pillar_cache_ttl": 30,
+            }
+        }
+    }
+
+
+@pytest.fixture(scope="module")
+def cachedir_tree(tmp_path_factory):
+    _cachedir_tree = tmp_path_factory.mktemp("cachedir")
+    try:
+        yield _cachedir_tree
+    finally:
+        shutil.rmtree(str(_cachedir_tree), ignore_errors=True)
+
+
+def test_clear_pillar_cache(cachedir_tree):
+    """
+    test pillar.clear_pillar_cache
+    """
+
+    cache = {
+        "CacheDisk_data": {
+            MINION_ID: {
+                None: {
+                    "this": "one",
+                    "that": "two",
+                    "those": ["three", "four", "five"],
+                }
+            }
+        },
+        "CacheDisk_cachetime": {MINION_ID: 1612302460.146923},
+    }
+    packer = salt.utils.msgpack.Packer()
+    cache_contents = packer.pack(cache)
+
+    with patch.dict(pillar_runner.__opts__, {"cachedir": str(cachedir_tree)}):
+        with patch(
+            "salt.utils.minions.CkMinions.check_minions",
+            MagicMock(return_value=_CHECK_MINIONS_RETURN),
+        ), patch("salt.utils.atomicfile.atomic_open", mock_open()) as atomic_open_mock:
+            with patch("os.path.exists", MagicMock(return_value=True)):
+                with patch(
+                    "salt.utils.files.fopen", mock_open(read_data=cache_contents)
+                ) as fopen_mock:
+                    # No arguments defaults to all globbing
+                    ret = pillar_runner.clear_pillar_cache()
+                    assert ret == {}
+
+                    # Also test passing specific minion
+                    ret = pillar_runner.clear_pillar_cache("test-host")
+                    assert ret == {}
+
+
+def test_show_pillar_cache(cachedir_tree):
+    """
+    test pillar.clear_pillar_cache
+    """
+
+    cache = {
+        "CacheDisk_data": {
+            MINION_ID: {
+                None: {
+                    "this": "one",
+                    "that": "two",
+                    "those": ["three", "four", "five"],
+                }
+            }
+        },
+        "CacheDisk_cachetime": {MINION_ID: 1612302460.146923},
+    }
+    packer = salt.utils.msgpack.Packer()
+    cache_contents = packer.pack(cache)
+
+    expected = {
+        MINION_ID: {"this": "one", "that": "two", "those": ["three", "four", "five"]}
+    }
+
+    with patch.dict(pillar_runner.__opts__, {"cachedir": str(cachedir_tree)}):
+        with patch(
+            "salt.utils.minions.CkMinions.check_minions",
+            MagicMock(return_value=_CHECK_MINIONS_RETURN),
+        ), patch("salt.utils.atomicfile.atomic_open", mock_open()) as atomic_open_mock:
+            with patch("os.path.exists", MagicMock(return_value=True)):
+                with patch(
+                    "salt.utils.files.fopen", mock_open(read_data=cache_contents)
+                ) as fopen_mock:
+                    ret = pillar_runner.show_pillar_cache()
+                    assert ret == expected
+
+                    ret = pillar_runner.show_pillar_cache("test-host")
+                    assert ret == expected
+
+        _EMPTY_CHECK_MINIONS_RETURN = {"minions": [], "missing": []}
+        with patch(
+            "salt.utils.minions.CkMinions.check_minions",
+            MagicMock(return_value=_EMPTY_CHECK_MINIONS_RETURN),
+        ), patch("salt.utils.atomicfile.atomic_open", mock_open()) as atomic_open_mock:
+            ret = pillar_runner.show_pillar_cache("fake-host")
+            assert ret == {}


Code review comments:
diff_hunk: @@ -97,3 +98,103 @@ def show_pillar(minion="*", **kwargs):
 
     compiled_pillar = pillar.compile_pillar()
     return compiled_pillar
+
+
+def clear_pillar_cache(minion="*", **kwargs):
+    """
+    Clears the cached values when using pillar_cache
+
+    CLI Example:
+
+    Clears the pillar cache for a specific minion:
+
+    .. code-block:: bash
+
+        salt-run pillar.clear_pillar_cache 'minion'
+
+    """
+
+    if not __opts__.get("pillar_cache"):
+        log.info("The pillar_cache is set to False or not enabled.")
+        return False
+
+    ckminions = salt.utils.minions.CkMinions(__opts__)
+    ret = ckminions.check_minions(minion)
+
+    pillarenv = kwargs.pop("pillarenv", None)
+    saltenv = kwargs.pop("saltenv", None)
+
+    pillar_cache = {}
+    for tgt in ret.get("minions", []):
+        saltenv = "base"
+        id_, grains, _ = salt.utils.minions.get_minion_data(tgt, __opts__)
+
+        for key in kwargs:
+            grains[key] = kwargs[key]
+
+        if grains is None:
+            grains = {"fqdn": minion}
+
+        pillar = salt.pillar.PillarCache(
+            __opts__, grains, id_, saltenv, pillarenv=pillarenv
+        )
+        pillar.clear_pillar()
+
+        if __opts__.get("pillar_cache_backend") == "memory":
+            _pillar_cache = pillar.cache
+        else:
+            _pillar_cache = pillar.cache._dict
+
+        if tgt in _pillar_cache and _pillar_cache[tgt]:
+            pillar_cache[tgt] = _pillar_cache.get(tgt).get(pillarenv)
+
+    return pillar_cache
+
+
+def show_pillar_cache(minion="*", **kwargs):
+    """
+    Shows the cached values in pillar_cache
+
+    CLI Example:
+
+    Shows the pillar cache for a specific minion:
+
+    .. code-block:: bash
+
+        salt-run pillar.show_pillar_cache 'minion'
+
+    """
+
+    if not __opts__.get("pillar_cache"):
+        log.info("The pillar_cache is set to False or not enabled.")
+        return False
+
+    ckminions = salt.utils.minions.CkMinions(__opts__)
+    ret = ckminions.check_minions(minion)
+
+    pillarenv = kwargs.pop("pillarenv", None)
+    saltenv = kwargs.pop("saltenv", "base")
+
+    pillar_cache = {}
+    for tgt in ret.get("minions", []):
+        id_, grains, _ = salt.utils.minions.get_minion_data(tgt, __opts__)
+
+        for key in kwargs:
+            grains[key] = kwargs[key]
+
+        if grains is None:
+            grains = {"fqdn": minion}
+
+        pillar = salt.pillar.PillarCache(
+            __opts__, grains, id_, saltenv, pillarenv=pillarenv
+        )
+
+        if __opts__.get("pillar_cache_backend") == "memory":
+            _pillar_cache = pillar.cache
+        else:
+            _pillar_cache = pillar.cache._dict
+
+        if tgt in _pillar_cache and _pillar_cache[tgt]:
+            pillar_cache[tgt] = _pillar_cache.get(tgt).get(pillarenv)
path: salt/runners/pillar.py
text: @user1:
```suggestion
            pillar_cache[tgt] = _pillar_cache[tgt].get(pillarenv)
```
At this stage we now `tgt` is in the pillar cache.